@@ -219,10 +219,13 @@ import (
219
219
"os"
220
220
"path/filepath"
221
221
"strings"
222
+ "sync"
222
223
"time"
223
224
224
225
"github.com/insomniacslk/dhcp/dhcpv4"
225
226
"github.com/insomniacslk/dhcp/dhcpv4/nclient4"
227
+ "github.com/insomniacslk/dhcp/dhcpv6"
228
+ "github.com/insomniacslk/dhcp/dhcpv6/nclient6"
226
229
"github.com/sirupsen/logrus"
227
230
"github.com/spf13/cobra"
228
231
@@ -234,7 +237,11 @@ import (
234
237
)
235
238
236
239
type cmdForknet struct {
237
- global * cmdGlobal
240
+ global * cmdGlobal
241
+ dhcpv4Lease * nclient4.Lease
242
+ dhcpv6Lease * dhcpv6.Message
243
+ applyDNSMu sync.Mutex
244
+ args []string
238
245
}
239
246
240
247
func (c * cmdForknet ) command () * cobra.Command {
@@ -298,6 +305,7 @@ func (c *cmdForknet) runInfo(_ *cobra.Command, _ []string) error {
298
305
func (c * cmdForknet ) runDHCP (_ * cobra.Command , args []string ) error {
299
306
logger := logrus .New ()
300
307
logger .Level = logrus .DebugLevel
308
+ c .args = args
301
309
302
310
if C .forknet_dhcp_logfile >= 0 {
303
311
logger .SetOutput (os .NewFile (uintptr (C .forknet_dhcp_logfile ), "incus-dhcp-logfile" ))
@@ -317,7 +325,7 @@ func (c *cmdForknet) runDHCP(_ *cobra.Command, args []string) error {
317
325
err := link .SetUp ()
318
326
if err != nil {
319
327
logger .WithField ("interface" , iface ).Error ("Giving up on DHCP, couldn't bring up interface" )
320
- return nil
328
+ return err
321
329
}
322
330
323
331
// Read the hostname.
@@ -328,67 +336,62 @@ func (c *cmdForknet) runDHCP(_ *cobra.Command, args []string) error {
328
336
329
337
hostname := strings .TrimSpace (string (bb ))
330
338
339
+ // Create PID file.
340
+ err = os .WriteFile (filepath .Join (args [0 ], "dhcp.pid" ), []byte (fmt .Sprintf ("%d" , os .Getpid ())), 0o644 )
341
+ if err != nil {
342
+ logger .WithError (err ).Error ("Giving up on DHCP, couldn't write PID file" )
343
+ return err
344
+ }
345
+
346
+ errorChannel := make (chan error , 2 )
347
+ go c .dhcpRunV4 (errorChannel , iface , hostname , logger )
348
+ go c .dhcpRunV6 (errorChannel , iface , hostname , logger )
349
+
350
+ err = <- errorChannel
351
+ return err
352
+ }
353
+
354
+ func (c * cmdForknet ) dhcpRunV4 (errorChannel chan error , iface string , hostname string , logger * logrus.Logger ) {
331
355
// Try to get a lease.
332
356
client , err := nclient4 .New (iface )
333
357
if err != nil {
334
- logger .WithError (err ).Error ("Giving up on DHCP, couldn't set up client" )
335
- return nil
358
+ logger .WithError (err ).Error ("Giving up on DHCPv4, couldn't set up client" )
359
+ errorChannel <- err
360
+ return
336
361
}
337
362
338
363
defer func () { _ = client .Close () }()
339
364
340
365
lease , err := client .Request (context .Background (), dhcpv4 .WithOption (dhcpv4 .OptHostName (hostname )))
341
366
if err != nil {
342
367
logger .WithError (err ).WithField ("hostname" , hostname ).
343
- Error ("Giving up on DHCP, couldn't get a lease" )
344
- return nil
368
+ Error ("Giving up on DHCPv4, couldn't get a lease" )
369
+ errorChannel <- err
370
+ return
345
371
}
346
372
347
373
// Parse the response.
348
374
if lease .Offer == nil {
349
375
logger .WithField ("hostname" , hostname ).
350
376
Error ("Giving up on DHCP, couldn't get a lease after 5s" )
351
- return nil
377
+ errorChannel <- err
378
+ return
352
379
}
353
380
354
381
if lease .Offer .YourIPAddr == nil || lease .Offer .YourIPAddr .Equal (net .IPv4zero ) || lease .Offer .SubnetMask () == nil || len (lease .Offer .Router ()) != 1 {
355
382
logger .Error ("Giving up on DHCP, lease didn't contain required fields" )
356
- return nil
383
+ errorChannel <- fmt .Errorf ("Giving up on DHCP, lease didn't contain required fields" )
384
+ return
357
385
}
358
386
359
- if len (lease .Offer .DNS ()) > 0 {
360
- // DNS configuration.
361
- f , err := os .Create (filepath .Join (args [0 ], "resolv.conf" ))
362
- if err != nil {
363
- logger .WithError (err ).Error ("Giving up on DHCP, couldn't create resolv.conf" )
364
- return nil
365
- }
366
-
367
- defer f .Close ()
368
-
369
- for _ , nameserver := range lease .Offer .DNS () {
370
- _ , err = fmt .Fprintf (f , "nameserver %s\n " , nameserver )
371
- if err != nil {
372
- logger .WithError (err ).Error ("Giving up on DHCP, couldn't prepare resolv.conf" )
373
- return nil
374
- }
375
- }
376
-
377
- if lease .Offer .DomainName () != "" {
378
- _ , err = fmt .Fprintf (f , "domain %s\n " , lease .Offer .DomainName ())
379
- if err != nil {
380
- logger .WithError (err ).Error ("Giving up on DHCP, couldn't prepare resolv.conf" )
381
- return nil
382
- }
383
- }
384
-
385
- if lease .Offer .DomainSearch () != nil && len (lease .Offer .DomainSearch ().Labels ) > 0 {
386
- _ , err = fmt .Fprintf (f , "search %s\n " , strings .Join (lease .Offer .DomainSearch ().Labels , ", " ))
387
- if err != nil {
388
- logger .WithError (err ).Error ("Giving up on DHCP, couldn't prepare resolv.conf" )
389
- return nil
390
- }
391
- }
387
+ c .applyDNSMu .Lock ()
388
+ c .dhcpv4Lease = lease
389
+ c .applyDNSMu .Unlock ()
390
+ err = c .dhcpApplyDNS (logger )
391
+ if err != nil {
392
+ logger .WithError (err ).Error ("Giving up on DHCPv4, error applying DNS" )
393
+ errorChannel <- err
394
+ return
392
395
}
393
396
394
397
// Network configuration.
@@ -402,8 +405,9 @@ func (c *cmdForknet) runDHCP(_ *cobra.Command, args []string) error {
402
405
403
406
err = addr .Add ()
404
407
if err != nil {
405
- logger .WithError (err ).Error ("Giving up on DHCP, couldn't add IP" )
406
- return nil
408
+ logger .WithError (err ).Error ("Giving up on DHCPv4, couldn't add IP" )
409
+ errorChannel <- err
410
+ return
407
411
}
408
412
409
413
if lease .Offer .Options .Has (dhcpv4 .OptionClasslessStaticRoute ) {
@@ -421,7 +425,8 @@ func (c *cmdForknet) runDHCP(_ *cobra.Command, args []string) error {
421
425
err = route .Add ()
422
426
if err != nil {
423
427
logger .WithError (err ).Error ("Giving up on DHCP, couldn't add classless static route" )
424
- return nil
428
+ errorChannel <- err
429
+ return
425
430
}
426
431
}
427
432
} else {
@@ -435,17 +440,11 @@ func (c *cmdForknet) runDHCP(_ *cobra.Command, args []string) error {
435
440
err = route .Add ()
436
441
if err != nil {
437
442
logger .WithError (err ).Error ("Giving up on DHCP, couldn't add default route" )
438
- return nil
443
+ errorChannel <- err
444
+ return
439
445
}
440
446
}
441
447
442
- // Create PID file.
443
- err = os .WriteFile (filepath .Join (args [0 ], "dhcp.pid" ), []byte (fmt .Sprintf ("%d" , os .Getpid ())), 0o644 )
444
- if err != nil {
445
- logger .WithError (err ).Error ("Giving up on DHCP, couldn't write PID file" )
446
- return nil
447
- }
448
-
449
448
// Handle DHCP renewal.
450
449
for {
451
450
// Wait until it's renewal time.
@@ -454,14 +453,175 @@ func (c *cmdForknet) runDHCP(_ *cobra.Command, args []string) error {
454
453
// Renew the lease.
455
454
newLease , err := client .Renew (context .Background (), lease , dhcpv4 .WithOption (dhcpv4 .OptHostName (hostname )))
456
455
if err != nil {
457
- logger .WithError (err ).Error ("Giving up on DHCP, couldn't renew the lease" )
458
- return nil
456
+ logger .WithError (err ).Error ("Giving up on DHCPv4, couldn't renew the lease" )
457
+ errorChannel <- err
458
+ return
459
459
}
460
460
461
461
lease = newLease
462
462
}
463
463
}
464
464
465
+ func (c * cmdForknet ) dhcpRunV6 (errorChannel chan error , iface string , hostname string , logger * logrus.Logger ) {
466
+ // Get a new DHCPv6 client.
467
+ client , err := nclient6 .New (iface )
468
+ if err != nil {
469
+ logger .WithError (err ).Error ("Giving up on DHCPv6, couldn't set up client" )
470
+ errorChannel <- err
471
+ return
472
+ }
473
+
474
+ defer func () { _ = client .Close () }()
475
+
476
+ // Try to get a lease.
477
+ advertisement , err := client .Solicit (context .Background (), dhcpv6 .WithFQDN (0 , hostname ))
478
+ if err != nil {
479
+ logger .WithError (err ).Error ("Giving up on DHCPv6, error during DHCPv6 Solicit" )
480
+ errorChannel <- err
481
+ return
482
+ }
483
+
484
+ reply , err := client .Request (context .Background (), advertisement , dhcpv6 .WithFQDN (0 , hostname ))
485
+ if err != nil {
486
+ logger .WithError (err ).Error ("Giving up on DHCPv6, error during DHCPv6 Request" )
487
+ errorChannel <- err
488
+ return
489
+ }
490
+
491
+ c .applyDNSMu .Lock ()
492
+ c .dhcpv6Lease = reply
493
+ c .applyDNSMu .Unlock ()
494
+ err = c .dhcpApplyDNS (logger )
495
+ if err != nil {
496
+ logger .WithError (err ).Error ("Giving up on DHCPv6, error applying DNS" )
497
+ errorChannel <- err
498
+ return
499
+ }
500
+
501
+ // Network configuration.
502
+ iana := reply .Options .OneIANA ()
503
+ if iana == nil {
504
+ logger .Error ("Giving up on DHCPv6 renewal, reply missing IANA" )
505
+ errorChannel <- fmt .Errorf ("Giving up on DHCPv6 renewal, reply missing IANA" )
506
+ return
507
+ }
508
+
509
+ for _ , iaaddr := range iana .Options .Addresses () {
510
+ addr := & ip.Addr {
511
+ DevName : iface ,
512
+ Address : fmt .Sprintf ("%s/%d" , iaaddr .IPv6Addr , net .CIDRMask (128 , 128 )),
513
+ Family : ip .FamilyV6 ,
514
+ }
515
+
516
+ err = addr .Add ()
517
+ if err != nil {
518
+ logger .WithError (err ).Error ("Giving up on DHCPv6, couldn't add IP" )
519
+ errorChannel <- err
520
+ return
521
+ }
522
+ }
523
+
524
+ // Handle DHCP Renewal.
525
+ for {
526
+ // Wait until it's renewal time.
527
+ t1 := iana .T1
528
+ time .Sleep (t1 )
529
+
530
+ // Renew the lease.
531
+ var optIAAddrs []dhcpv6.OptIAAddress
532
+ for _ , optIAAddr := range iana .Options .Addresses () {
533
+ optIAAddrs = append (optIAAddrs , * optIAAddr )
534
+ }
535
+
536
+ modifiers := []dhcpv6.Modifier {
537
+ dhcpv6 .WithClientID (reply .Options .ClientID ()),
538
+ dhcpv6 .WithServerID (reply .Options .ServerID ()),
539
+ dhcpv6 .WithIAID (iana .IaId ),
540
+ dhcpv6 .WithIANA (optIAAddrs ... ),
541
+ }
542
+
543
+ renew , err := dhcpv6 .NewMessage (modifiers ... )
544
+ if err != nil {
545
+ logger .WithError (err ).Error ("Giving up on DHCv6, couldn't create renew message" )
546
+ errorChannel <- err
547
+ return
548
+ }
549
+
550
+ renew .MessageType = dhcpv6 .MessageTypeRenew
551
+
552
+ newReply , err := dhcpv6 .NewReplyFromMessage (renew , dhcpv6 .WithFQDN (0 , hostname ))
553
+ if err != nil {
554
+ logger .WithError (err ).Error ("Giving up on DHCPv6, couldn't renew the lease" )
555
+ errorChannel <- err
556
+ return
557
+ }
558
+
559
+ reply = newReply
560
+ }
561
+ }
562
+
563
+ func (c * cmdForknet ) dhcpApplyDNS (logger * logrus.Logger ) error {
564
+ c .applyDNSMu .Lock ()
565
+ defer c .applyDNSMu .Unlock ()
566
+
567
+ f , err := os .Create (filepath .Join (c .args [0 ], "resolv.conf" ))
568
+ if err != nil {
569
+ logger .WithError (err ).Error ("Giving up on DHCP, couldn't create resolv.conf" )
570
+ return err
571
+ }
572
+
573
+ defer f .Close ()
574
+
575
+ if c .dhcpv4Lease != nil {
576
+ if len (c .dhcpv4Lease .Offer .DNS ()) > 0 {
577
+ for _ , nameserver := range c .dhcpv4Lease .Offer .DNS () {
578
+ _ , err = fmt .Fprintf (f , "nameserver %s\n " , nameserver )
579
+ if err != nil {
580
+ logger .WithError (err ).Error ("Giving up on DHCPv4, couldn't prepare resolv.conf" )
581
+ return err
582
+ }
583
+ }
584
+
585
+ if c .dhcpv4Lease .Offer .DomainName () != "" {
586
+ _ , err = fmt .Fprintf (f , "domain %s\n " , c .dhcpv4Lease .Offer .DomainName ())
587
+ if err != nil {
588
+ logger .WithError (err ).Error ("Giving up on DHCPv4, couldn't prepare resolv.conf" )
589
+ return err
590
+ }
591
+ }
592
+
593
+ if c .dhcpv4Lease .Offer .DomainSearch () != nil && len (c .dhcpv4Lease .Offer .DomainSearch ().Labels ) > 0 {
594
+ _ , err = fmt .Fprintf (f , "search %s\n " , strings .Join (c .dhcpv4Lease .Offer .DomainSearch ().Labels , ", " ))
595
+ if err != nil {
596
+ logger .WithError (err ).Error ("Giving up on DHCPv4, couldn't prepare resolv.conf" )
597
+ return err
598
+ }
599
+ }
600
+ }
601
+ }
602
+
603
+ if c .dhcpv6Lease != nil {
604
+ if len (c .dhcpv6Lease .Options .DNS ()) > 0 {
605
+ for _ , nameserver := range c .dhcpv6Lease .Options .DNS () {
606
+ _ , err = fmt .Fprintf (f , "nameserver %s\n " , nameserver )
607
+ if err != nil {
608
+ logger .WithError (err ).Error ("Giving up on DHCPv6, couldn't prepare resolv.conf" )
609
+ return err
610
+ }
611
+ }
612
+
613
+ if c .dhcpv6Lease .Options .DomainSearchList () != nil && len (c .dhcpv6Lease .Options .DomainSearchList ().Labels ) > 0 {
614
+ _ , err = fmt .Fprintf (f , "search %s\n " , strings .Join (c .dhcpv6Lease .Options .DomainSearchList ().Labels , ", " ))
615
+ if err != nil {
616
+ logger .WithError (err ).Error ("Giving up on DHCPv6, couldn't prepare resolv.conf" )
617
+ return err
618
+ }
619
+ }
620
+ }
621
+ }
622
+ return nil
623
+ }
624
+
465
625
func (c * cmdForknet ) runDetach (_ * cobra.Command , args []string ) error {
466
626
daemonPID := args [1 ]
467
627
ifName := args [2 ]
0 commit comments