Przeglądaj źródła

[ipv6] Add IPv6 network device configurator

Include IPv6 within the generic network device configurator
mechanism.  The IPv6 configurator will send a router solicitation and
wait for a router advertisement to be received.  (As per RFC4861
section 6.3.7, we do this even if advertisements have been received
prior to sending the router solicitation.)

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 11 lat temu
rodzic
commit
6871a8113f
2 zmienionych plików z 217 dodań i 28 usunięć
  1. 0
    2
      src/include/ipxe/ndp.h
  2. 217
    26
      src/net/ndp.c

+ 0
- 2
src/include/ipxe/ndp.h Wyświetl plik

@@ -166,6 +166,4 @@ static inline int ndp_tx ( struct io_buffer *iobuf, struct net_device *netdev,
166 166
 			      &ndp_discovery, net_source, ll_source );
167 167
 }
168 168
 
169
-extern int ndp_tx_router_solicitation ( struct net_device *netdev );
170
-
171 169
 #endif /* _IPXE_NDP_H */

+ 217
- 26
src/net/ndp.c Wyświetl plik

@@ -19,6 +19,7 @@
19 19
 
20 20
 FILE_LICENCE ( GPL2_OR_LATER );
21 21
 
22
+#include <stdlib.h>
22 23
 #include <string.h>
23 24
 #include <errno.h>
24 25
 #include <byteswap.h>
@@ -36,6 +37,9 @@ FILE_LICENCE ( GPL2_OR_LATER );
36 37
  *
37 38
  */
38 39
 
40
+static int ipv6conf_rx_router_advertisement ( struct net_device *netdev,
41
+					      unsigned int flags );
42
+
39 43
 /**
40 44
  * Transmit NDP packet with link-layer address option
41 45
  *
@@ -147,7 +151,7 @@ struct neighbour_discovery ndp_discovery = {
147 151
  * @v netdev		Network device
148 152
  * @ret rc		Return status code
149 153
  */
150
-int ndp_tx_router_solicitation ( struct net_device *netdev ) {
154
+static int ndp_tx_router_solicitation ( struct net_device *netdev ) {
151 155
 	struct ndp_router_solicitation_header rsol;
152 156
 	struct sockaddr_in6 sin6_dest;
153 157
 	int rc;
@@ -443,35 +447,33 @@ static int ndp_rx_option ( struct net_device *netdev,
443 447
 }
444 448
 
445 449
 /**
446
- * Process received NDP packet
450
+ * Process received NDP packet options
447 451
  *
448
- * @v iobuf		I/O buffer
449 452
  * @v netdev		Network device
450 453
  * @v sin6_src		Source socket address
454
+ * @v ndp		NDP header
451 455
  * @v offset		Offset to NDP options
456
+ * @v len		Length of NDP packet
452 457
  * @ret rc		Return status code
453 458
  */
454
-static int ndp_rx ( struct io_buffer *iobuf,
455
-		    struct net_device *netdev,
456
-		    struct sockaddr_in6 *sin6_src,
457
-		    size_t offset ) {
458
-	union ndp_header *ndp = iobuf->data;
459
+static int ndp_rx_options ( struct net_device *netdev,
460
+			    struct sockaddr_in6 *sin6_src,
461
+			    union ndp_header *ndp, size_t offset, size_t len ) {
459 462
 	union ndp_option *option;
460 463
 	size_t remaining;
461 464
 	size_t option_len;
462 465
 	int rc;
463 466
 
464 467
 	/* Sanity check */
465
-	if ( iob_len ( iobuf ) < offset ) {
468
+	if ( len < offset ) {
466 469
 		DBGC ( netdev, "NDP packet too short at %zd bytes (min %zd "
467
-		       "bytes)\n", iob_len ( iobuf ), offset );
468
-		rc = -EINVAL;
469
-		goto done;
470
+		       "bytes)\n", len, offset );
471
+		return -EINVAL;
470 472
 	}
471 473
 
472 474
 	/* Search for option */
473 475
 	option = ( ( ( void * ) ndp ) + offset );
474
-	remaining = ( iob_len ( iobuf ) - offset );
476
+	remaining = ( len - offset );
475 477
 	while ( remaining ) {
476 478
 
477 479
 		/* Sanity check */
@@ -481,27 +483,21 @@ static int ndp_rx ( struct io_buffer *iobuf,
481 483
 				     NDP_OPTION_BLKSZ ) ) ) {
482 484
 			DBGC ( netdev, "NDP bad option length:\n" );
483 485
 			DBGC_HDA ( netdev, 0, option, remaining );
484
-			rc = -EINVAL;
485
-			goto done;
486
+			return -EINVAL;
486 487
 		}
487 488
 		option_len = ( option->header.blocks * NDP_OPTION_BLKSZ );
488 489
 
489 490
 		/* Handle option */
490 491
 		if ( ( rc = ndp_rx_option ( netdev, sin6_src, ndp, option,
491 492
 					    option_len ) ) != 0 )
492
-			goto done;
493
+			return rc;
493 494
 
494 495
 		/* Move to next option */
495 496
 		option = ( ( ( void * ) option ) + option_len );
496 497
 		remaining -= option_len;
497 498
 	}
498 499
 
499
-	/* Success */
500
-	rc = 0;
501
-
502
- done:
503
-	free_iob ( iobuf );
504
-	return rc;
500
+	return 0;
505 501
 }
506 502
 
507 503
 /**
@@ -519,9 +515,18 @@ static int ndp_rx_neighbour ( struct io_buffer *iobuf,
519 515
 			      struct sockaddr_in6 *sin6_dest __unused ) {
520 516
 	union ndp_header *ndp = iobuf->data;
521 517
 	struct ndp_neighbour_header *neigh = &ndp->neigh;
518
+	size_t len = iob_len ( iobuf );
519
+	int rc;
520
+
521
+	/* Process options */
522
+	if ( ( rc = ndp_rx_options ( netdev, sin6_src, ndp,
523
+				     offsetof ( typeof ( *neigh ), option ),
524
+				     len ) ) != 0 )
525
+		goto err_options;
522 526
 
523
-	return ndp_rx ( iobuf, netdev, sin6_src,
524
-			offsetof ( typeof ( *neigh ), option ) );
527
+ err_options:
528
+	free_iob ( iobuf );
529
+	return rc;
525 530
 }
526 531
 
527 532
 /**
@@ -540,9 +545,24 @@ ndp_rx_router_advertisement ( struct io_buffer *iobuf,
540 545
 			      struct sockaddr_in6 *sin6_dest __unused ) {
541 546
 	union ndp_header *ndp = iobuf->data;
542 547
 	struct ndp_router_advertisement_header *radv = &ndp->radv;
548
+	size_t len = iob_len ( iobuf );
549
+	int rc;
543 550
 
544
-	return ndp_rx ( iobuf, netdev, sin6_src,
545
-			offsetof ( typeof ( *radv ), option ) );
551
+	/* Process options */
552
+	if ( ( rc = ndp_rx_options ( netdev, sin6_src, ndp,
553
+				     offsetof ( typeof ( *radv ), option ),
554
+				     len ) ) != 0 )
555
+		goto err_options;
556
+
557
+	/* Pass to IPv6 autoconfiguration */
558
+	if ( ( rc = ipv6conf_rx_router_advertisement ( netdev,
559
+						       radv->flags ) ) != 0 )
560
+		goto err_ipv6conf;
561
+
562
+ err_ipv6conf:
563
+ err_options:
564
+	free_iob ( iobuf );
565
+	return rc;
546 566
 }
547 567
 
548 568
 /** NDP ICMPv6 handlers */
@@ -560,3 +580,174 @@ struct icmpv6_handler ndp_handlers[] __icmpv6_handler = {
560 580
 		.rx = ndp_rx_router_advertisement,
561 581
 	},
562 582
 };
583
+
584
+/****************************************************************************
585
+ *
586
+ * IPv6 autoconfiguration
587
+ *
588
+ */
589
+
590
+/** An IPv6 configurator */
591
+struct ipv6conf {
592
+	/** Reference count */
593
+	struct refcnt refcnt;
594
+	/** List of configurators */
595
+	struct list_head list;
596
+
597
+	/** Job control interface */
598
+	struct interface job;
599
+
600
+	/** Network device being configured */
601
+	struct net_device *netdev;
602
+
603
+	/** Retransmission timer */
604
+	struct retry_timer timer;
605
+};
606
+
607
+/** List of IPv6 configurators */
608
+static LIST_HEAD ( ipv6confs );
609
+
610
+/**
611
+ * Free IPv6 configurator
612
+ *
613
+ * @v refcnt		Reference count
614
+ */
615
+static void ipv6conf_free ( struct refcnt *refcnt ) {
616
+	struct ipv6conf *ipv6conf =
617
+		container_of ( refcnt, struct ipv6conf, refcnt );
618
+
619
+	netdev_put ( ipv6conf->netdev );
620
+	free ( ipv6conf );
621
+}
622
+
623
+/**
624
+ * Identify IPv6 configurator by network device
625
+ *
626
+ * @v netdev		Network device
627
+ * @ret ipv6		IPv6 configurator, or NULL
628
+ */
629
+static struct ipv6conf * ipv6conf_demux ( struct net_device *netdev ) {
630
+	struct ipv6conf *ipv6conf;
631
+
632
+	list_for_each_entry ( ipv6conf, &ipv6confs, list ) {
633
+		if ( ipv6conf->netdev == netdev )
634
+			return ipv6conf;
635
+	}
636
+	return NULL;
637
+}
638
+
639
+/**
640
+ * Finish IPv6 autoconfiguration
641
+ *
642
+ * @v ipv6		IPv6 configurator
643
+ * @v rc		Reason for finishing
644
+ */
645
+static void ipv6conf_done ( struct ipv6conf *ipv6conf, int rc ) {
646
+
647
+	/* Shut down interfaces */
648
+	intf_shutdown ( &ipv6conf->job, rc );
649
+
650
+	/* Stop timer */
651
+	stop_timer ( &ipv6conf->timer );
652
+
653
+	/* Remove from list and drop list's reference */
654
+	list_del ( &ipv6conf->list );
655
+	ref_put ( &ipv6conf->refcnt );
656
+}
657
+
658
+/**
659
+ * Handle IPv6 configurator timer expiry
660
+ *
661
+ * @v timer		Retry timer
662
+ * @v fail		Failure indicator
663
+ */
664
+static void ipv6conf_expired ( struct retry_timer *timer, int fail ) {
665
+	struct ipv6conf *ipv6conf =
666
+		container_of ( timer, struct ipv6conf, timer );
667
+
668
+	/* If we have failed, terminate autoconfiguration */
669
+	if ( fail ) {
670
+		ipv6conf_done ( ipv6conf, -ETIMEDOUT );
671
+		return;
672
+	}
673
+
674
+	/* Otherwise, transmit router solicitation and restart timer */
675
+	start_timer ( &ipv6conf->timer );
676
+	ndp_tx_router_solicitation ( ipv6conf->netdev );
677
+}
678
+
679
+/**
680
+ * Handle router advertisement during IPv6 autoconfiguration
681
+ *
682
+ * @v netdev		Network device
683
+ * @v flags		Router flags
684
+ * @ret rc		Return status code
685
+ */
686
+static int ipv6conf_rx_router_advertisement ( struct net_device *netdev,
687
+					      unsigned int flags ) {
688
+	struct ipv6conf *ipv6conf;
689
+
690
+	/* Identify IPv6 configurator, if any */
691
+	ipv6conf = ipv6conf_demux ( netdev );
692
+	if ( ! ipv6conf ) {
693
+		/* Not an error; router advertisements are processed
694
+		 * as a background activity even when no explicit
695
+		 * autoconfiguration is taking place.
696
+		 */
697
+		return 0;
698
+	}
699
+
700
+	/* Fail if stateful address autoconfiguration is required */
701
+	if ( flags & NDP_ROUTER_MANAGED ) {
702
+		ipv6conf_done ( ipv6conf, -ENOTSUP );
703
+		return -ENOTSUP;
704
+	}
705
+
706
+	/* Mark autoconfiguration as complete */
707
+	ipv6conf_done ( ipv6conf, 0 );
708
+
709
+	return 0;
710
+}
711
+
712
+/** IPv6 configurator job interface operations */
713
+static struct interface_operation ipv6conf_job_op[] = {
714
+	INTF_OP ( intf_close, struct ipv6conf *, ipv6conf_done ),
715
+};
716
+
717
+/** IPv6 configurator job interface descriptor */
718
+static struct interface_descriptor ipv6conf_job_desc =
719
+	INTF_DESC ( struct ipv6conf, job, ipv6conf_job_op );
720
+
721
+/**
722
+ * Start IPv6 autoconfiguration
723
+ *
724
+ * @v job		Job control interface
725
+ * @v netdev		Network device
726
+ * @ret rc		Return status code
727
+ */
728
+int start_ipv6conf ( struct interface *job, struct net_device *netdev ) {
729
+	struct ipv6conf *ipv6conf;
730
+
731
+	/* Allocate and initialise structure */
732
+	ipv6conf = zalloc ( sizeof ( *ipv6conf ) );
733
+	if ( ! ipv6conf )
734
+		return -ENOMEM;
735
+	ref_init ( &ipv6conf->refcnt, ipv6conf_free );
736
+	intf_init ( &ipv6conf->job, &ipv6conf_job_desc, &ipv6conf->refcnt );
737
+	timer_init ( &ipv6conf->timer, ipv6conf_expired, &ipv6conf->refcnt );
738
+	ipv6conf->netdev = netdev_get ( netdev );
739
+
740
+	/* Start timer to initiate router solicitation */
741
+	start_timer_nodelay ( &ipv6conf->timer );
742
+
743
+	/* Attach parent interface, transfer reference to list, and return */
744
+	intf_plug_plug ( &ipv6conf->job, job );
745
+	list_add ( &ipv6conf->list, &ipv6confs );
746
+	return 0;
747
+}
748
+
749
+/** IPv6 network device configurator */
750
+struct net_device_configurator ipv6_configurator __net_device_configurator = {
751
+	.name = "ipv6",
752
+	.start = start_ipv6conf,
753
+};

Ładowanie…
Anuluj
Zapisz