|
@@ -17,12 +17,16 @@
|
17
|
17
|
*/
|
18
|
18
|
|
19
|
19
|
#include <string.h>
|
|
20
|
+#include <stdlib.h>
|
20
|
21
|
#include <errno.h>
|
21
|
22
|
#include <assert.h>
|
22
|
23
|
#include <byteswap.h>
|
23
|
24
|
#include <gpxe/if_ether.h>
|
24
|
25
|
#include <gpxe/netdevice.h>
|
25
|
|
-#include <gpxe/udp.h>
|
|
26
|
+#include <gpxe/xfer.h>
|
|
27
|
+#include <gpxe/open.h>
|
|
28
|
+#include <gpxe/job.h>
|
|
29
|
+#include <gpxe/retry.h>
|
26
|
30
|
#include <gpxe/dhcp.h>
|
27
|
31
|
|
28
|
32
|
/** @file
|
|
@@ -387,7 +391,6 @@ static void merge_dhcp_field ( struct dhcp_option_block *options,
|
387
|
391
|
/**
|
388
|
392
|
* Parse DHCP packet and construct DHCP options block
|
389
|
393
|
*
|
390
|
|
- * @v dhcp DHCP session
|
391
|
394
|
* @v dhcphdr DHCP packet
|
392
|
395
|
* @v len Length of DHCP packet
|
393
|
396
|
* @ret options DHCP options block, or NULL
|
|
@@ -407,8 +410,7 @@ static void merge_dhcp_field ( struct dhcp_option_block *options,
|
407
|
410
|
* options block; it is the responsibility of the caller to eventually
|
408
|
411
|
* free this memory.
|
409
|
412
|
*/
|
410
|
|
-static struct dhcp_option_block * dhcp_parse ( struct dhcp_session *dhcp,
|
411
|
|
- struct dhcphdr *dhcphdr,
|
|
413
|
+static struct dhcp_option_block * dhcp_parse ( const struct dhcphdr *dhcphdr,
|
412
|
414
|
size_t len ) {
|
413
|
415
|
struct dhcp_option_block *options;
|
414
|
416
|
size_t options_len;
|
|
@@ -443,8 +445,8 @@ static struct dhcp_option_block * dhcp_parse ( struct dhcp_session *dhcp,
|
443
|
445
|
/* Allocate empty options block of required size */
|
444
|
446
|
options = alloc_dhcp_options ( options_len );
|
445
|
447
|
if ( ! options ) {
|
446
|
|
- DBGC ( dhcp, "DHCP %p could not allocate %d-byte option "
|
447
|
|
- "block\n", dhcp, options_len );
|
|
448
|
+ DBG ( "DHCP could not allocate %d-byte option block\n",
|
|
449
|
+ options_len );
|
448
|
450
|
return NULL;
|
449
|
451
|
}
|
450
|
452
|
|
|
@@ -488,12 +490,45 @@ static struct dhcp_option_block * dhcp_parse ( struct dhcp_session *dhcp,
|
488
|
490
|
*
|
489
|
491
|
*/
|
490
|
492
|
|
491
|
|
-static inline struct dhcp_session *
|
492
|
|
-udp_to_dhcp ( struct udp_connection *conn ) {
|
493
|
|
- return container_of ( conn, struct dhcp_session, udp );
|
494
|
|
-}
|
|
493
|
+/** A DHCP session */
|
|
494
|
+struct dhcp_session {
|
|
495
|
+ /** Reference counter */
|
|
496
|
+ struct refcnt refcnt;
|
|
497
|
+ /** Job control interface */
|
|
498
|
+ struct job_interface job;
|
|
499
|
+ /** Data transfer interface */
|
|
500
|
+ struct xfer_interface xfer;
|
|
501
|
+
|
|
502
|
+ /** Network device being configured */
|
|
503
|
+ struct net_device *netdev;
|
|
504
|
+ /** Option block registration routine */
|
|
505
|
+ int ( * register_options ) ( struct dhcp_option_block *options );
|
|
506
|
+
|
|
507
|
+ /** State of the session
|
|
508
|
+ *
|
|
509
|
+ * This is a value for the @c DHCP_MESSAGE_TYPE option
|
|
510
|
+ * (e.g. @c DHCPDISCOVER).
|
|
511
|
+ */
|
|
512
|
+ int state;
|
|
513
|
+ /** Options obtained from server */
|
|
514
|
+ struct dhcp_option_block *options;
|
|
515
|
+ /** Retransmission timer */
|
|
516
|
+ struct retry_timer timer;
|
|
517
|
+};
|
|
518
|
+
|
|
519
|
+/**
|
|
520
|
+ * Free DHCP session
|
|
521
|
+ *
|
|
522
|
+ * @v refcnt Reference counter
|
|
523
|
+ */
|
|
524
|
+static void dhcp_free ( struct refcnt *refcnt ) {
|
|
525
|
+ struct dhcp_session *dhcp =
|
|
526
|
+ container_of ( refcnt, struct dhcp_session, refcnt );
|
495
|
527
|
|
496
|
|
-#if 0
|
|
528
|
+ netdev_put ( dhcp->netdev );
|
|
529
|
+ dhcpopt_put ( dhcp->options );
|
|
530
|
+ free ( dhcp );
|
|
531
|
+}
|
497
|
532
|
|
498
|
533
|
/**
|
499
|
534
|
* Mark DHCP session as complete
|
|
@@ -501,50 +536,38 @@ udp_to_dhcp ( struct udp_connection *conn ) {
|
501
|
536
|
* @v dhcp DHCP session
|
502
|
537
|
* @v rc Return status code
|
503
|
538
|
*/
|
504
|
|
-static void dhcp_done ( struct dhcp_session *dhcp, int rc ) {
|
|
539
|
+static void dhcp_finished ( struct dhcp_session *dhcp, int rc ) {
|
505
|
540
|
|
506
|
|
- /* Free up options if we failed */
|
507
|
|
- if ( rc != 0 ) {
|
508
|
|
- if ( dhcp->options ) {
|
509
|
|
- free_dhcp_options ( dhcp->options );
|
510
|
|
- dhcp->options = NULL;
|
511
|
|
- }
|
512
|
|
- }
|
|
541
|
+ /* Block futher incoming messages */
|
|
542
|
+ job_nullify ( &dhcp->job );
|
|
543
|
+ xfer_nullify ( &dhcp->xfer );
|
513
|
544
|
|
514
|
545
|
/* Stop retry timer */
|
515
|
546
|
stop_timer ( &dhcp->timer );
|
516
|
547
|
|
517
|
|
- /* Close UDP connection */
|
518
|
|
- udp_close ( &dhcp->udp );
|
519
|
|
-
|
520
|
|
- /* Mark async operation as complete */
|
521
|
|
- async_done ( &dhcp->async, rc );
|
|
548
|
+ /* Free resources and close interfaces */
|
|
549
|
+ xfer_close ( &dhcp->xfer, rc );
|
|
550
|
+ job_done ( &dhcp->job, rc );
|
522
|
551
|
}
|
523
|
552
|
|
524
|
|
-/** Address for transmitting DHCP requests */
|
525
|
|
-static union {
|
526
|
|
- struct sockaddr_tcpip st;
|
527
|
|
- struct sockaddr_in sin;
|
528
|
|
-} sa_dhcp_server = {
|
529
|
|
- .sin = {
|
530
|
|
- .sin_family = AF_INET,
|
531
|
|
- .sin_addr.s_addr = INADDR_BROADCAST,
|
532
|
|
- .sin_port = htons ( BOOTPS_PORT ),
|
533
|
|
- },
|
534
|
|
-};
|
|
553
|
+/****************************************************************************
|
|
554
|
+ *
|
|
555
|
+ * Data transfer interface
|
|
556
|
+ *
|
|
557
|
+ */
|
535
|
558
|
|
536
|
559
|
/**
|
537
|
560
|
* Transmit DHCP request
|
538
|
561
|
*
|
539
|
|
- * @v conn UDP connection
|
540
|
|
- * @v buf Temporary data buffer
|
541
|
|
- * @v len Length of temporary data buffer
|
|
562
|
+ * @v dhcp DHCP session
|
542
|
563
|
* @ret rc Return status code
|
543
|
564
|
*/
|
544
|
|
-static int dhcp_senddata ( struct udp_connection *conn,
|
545
|
|
- void *buf, size_t len ) {
|
546
|
|
- struct dhcp_session *dhcp = udp_to_dhcp ( conn );
|
|
565
|
+static int dhcp_send_request ( struct dhcp_session *dhcp ) {
|
|
566
|
+ struct xfer_metadata meta = {
|
|
567
|
+ .netdev = dhcp->netdev,
|
|
568
|
+ };
|
547
|
569
|
struct dhcp_packet dhcppkt;
|
|
570
|
+ struct io_buffer *iobuf;
|
548
|
571
|
int rc;
|
549
|
572
|
|
550
|
573
|
DBGC ( dhcp, "DHCP %p transmitting %s\n",
|
|
@@ -553,12 +576,23 @@ static int dhcp_senddata ( struct udp_connection *conn,
|
553
|
576
|
assert ( ( dhcp->state == DHCPDISCOVER ) ||
|
554
|
577
|
( dhcp->state == DHCPREQUEST ) );
|
555
|
578
|
|
|
579
|
+ /* Start retry timer. Do this first so that failures to
|
|
580
|
+ * transmit will be retried.
|
|
581
|
+ */
|
|
582
|
+ start_timer ( &dhcp->timer );
|
|
583
|
+
|
|
584
|
+ /* Allocate buffer for packet */
|
|
585
|
+ iobuf = xfer_alloc_iob ( &dhcp->xfer, DHCP_MIN_LEN );
|
|
586
|
+ if ( ! iobuf )
|
|
587
|
+ return -ENOMEM;
|
|
588
|
+
|
556
|
589
|
/* Create DHCP packet in temporary buffer */
|
557
|
|
- if ( ( rc = create_dhcp_packet ( dhcp->netdev, dhcp->state, buf, len,
|
|
590
|
+ if ( ( rc = create_dhcp_packet ( dhcp->netdev, dhcp->state,
|
|
591
|
+ iobuf->data, iob_tailroom ( iobuf ),
|
558
|
592
|
&dhcppkt ) ) != 0 ) {
|
559
|
593
|
DBGC ( dhcp, "DHCP %p could not create DHCP packet: %s\n",
|
560
|
594
|
dhcp, strerror ( rc ) );
|
561
|
|
- return rc;
|
|
595
|
+ goto done;
|
562
|
596
|
}
|
563
|
597
|
|
564
|
598
|
/* Copy in options common to all requests */
|
|
@@ -566,7 +600,7 @@ static int dhcp_senddata ( struct udp_connection *conn,
|
566
|
600
|
&dhcp_request_options ) ) != 0){
|
567
|
601
|
DBGC ( dhcp, "DHCP %p could not set common DHCP options: %s\n",
|
568
|
602
|
dhcp, strerror ( rc ) );
|
569
|
|
- return rc;
|
|
603
|
+ goto done;
|
570
|
604
|
}
|
571
|
605
|
|
572
|
606
|
/* Copy any required options from previous server repsonse */
|
|
@@ -576,36 +610,30 @@ static int dhcp_senddata ( struct udp_connection *conn,
|
576
|
610
|
DHCP_SERVER_IDENTIFIER ) ) != 0 ) {
|
577
|
611
|
DBGC ( dhcp, "DHCP %p could not set server identifier "
|
578
|
612
|
"option: %s\n", dhcp, strerror ( rc ) );
|
579
|
|
- return rc;
|
|
613
|
+ goto done;
|
580
|
614
|
}
|
581
|
615
|
if ( ( rc = copy_dhcp_packet_option ( &dhcppkt, dhcp->options,
|
582
|
616
|
DHCP_EB_YIADDR,
|
583
|
617
|
DHCP_REQUESTED_ADDRESS ) ) != 0 ) {
|
584
|
618
|
DBGC ( dhcp, "DHCP %p could not set requested address "
|
585
|
619
|
"option: %s\n", dhcp, strerror ( rc ) );
|
586
|
|
- return rc;
|
|
620
|
+ goto done;
|
587
|
621
|
}
|
588
|
622
|
}
|
589
|
623
|
|
590
|
624
|
/* Transmit the packet */
|
591
|
|
- if ( ( rc = udp_sendto_via ( conn, &sa_dhcp_server.st, dhcp->netdev,
|
592
|
|
- dhcppkt.dhcphdr, dhcppkt.len ) ) != 0 ) {
|
|
625
|
+ iob_put ( iobuf, dhcppkt.len );
|
|
626
|
+ rc = xfer_deliver_iob_meta ( &dhcp->xfer, iobuf, &meta );
|
|
627
|
+ iobuf = NULL;
|
|
628
|
+ if ( rc != 0 ) {
|
593
|
629
|
DBGC ( dhcp, "DHCP %p could not transmit UDP packet: %s\n",
|
594
|
630
|
dhcp, strerror ( rc ) );
|
595
|
|
- return rc;
|
|
631
|
+ goto done;
|
596
|
632
|
}
|
597
|
633
|
|
598
|
|
- return 0;
|
599
|
|
-}
|
600
|
|
-
|
601
|
|
-/**
|
602
|
|
- * Transmit DHCP request
|
603
|
|
- *
|
604
|
|
- * @v dhcp DHCP session
|
605
|
|
- */
|
606
|
|
-static void dhcp_send_request ( struct dhcp_session *dhcp ) {
|
607
|
|
- start_timer ( &dhcp->timer );
|
608
|
|
- udp_senddata ( &dhcp->udp );
|
|
634
|
+ done:
|
|
635
|
+ free_iob ( iobuf );
|
|
636
|
+ return rc;
|
609
|
637
|
}
|
610
|
638
|
|
611
|
639
|
/**
|
|
@@ -619,7 +647,7 @@ static void dhcp_timer_expired ( struct retry_timer *timer, int fail ) {
|
619
|
647
|
container_of ( timer, struct dhcp_session, timer );
|
620
|
648
|
|
621
|
649
|
if ( fail ) {
|
622
|
|
- dhcp_done ( dhcp, -ETIMEDOUT );
|
|
650
|
+ dhcp_finished ( dhcp, -ETIMEDOUT );
|
623
|
651
|
} else {
|
624
|
652
|
dhcp_send_request ( dhcp );
|
625
|
653
|
}
|
|
@@ -628,17 +656,17 @@ static void dhcp_timer_expired ( struct retry_timer *timer, int fail ) {
|
628
|
656
|
/**
|
629
|
657
|
* Receive new data
|
630
|
658
|
*
|
631
|
|
- * @v udp UDP connection
|
|
659
|
+ * @v xfer Data transfer interface
|
|
660
|
+ * @v iobuf I/O buffer
|
632
|
661
|
* @v data Received data
|
633
|
662
|
* @v len Length of received data
|
634
|
|
- * @v st_src Partially-filled source address
|
635
|
|
- * @v st_dest Partially-filled destination address
|
|
663
|
+ * @ret rc Return status code
|
636
|
664
|
*/
|
637
|
|
-static int dhcp_newdata ( struct udp_connection *conn, void *data, size_t len,
|
638
|
|
- struct sockaddr_tcpip *st_src __unused,
|
639
|
|
- struct sockaddr_tcpip *st_dest __unused ) {
|
640
|
|
- struct dhcp_session *dhcp = udp_to_dhcp ( conn );
|
641
|
|
- struct dhcphdr *dhcphdr = data;
|
|
665
|
+static int dhcp_deliver_raw ( struct xfer_interface *xfer,
|
|
666
|
+ const void *data, size_t len ) {
|
|
667
|
+ struct dhcp_session *dhcp =
|
|
668
|
+ container_of ( xfer, struct dhcp_session, xfer );
|
|
669
|
+ const struct dhcphdr *dhcphdr = data;
|
642
|
670
|
struct dhcp_option_block *options;
|
643
|
671
|
unsigned int msgtype;
|
644
|
672
|
|
|
@@ -651,7 +679,7 @@ static int dhcp_newdata ( struct udp_connection *conn, void *data, size_t len,
|
651
|
679
|
};
|
652
|
680
|
|
653
|
681
|
/* Parse packet and create options structure */
|
654
|
|
- options = dhcp_parse ( dhcp, dhcphdr, len );
|
|
682
|
+ options = dhcp_parse ( dhcphdr, len );
|
655
|
683
|
if ( ! options ) {
|
656
|
684
|
DBGC ( dhcp, "DHCP %p could not parse DHCP packet\n", dhcp );
|
657
|
685
|
return -EINVAL;
|
|
@@ -682,58 +710,120 @@ static int dhcp_newdata ( struct udp_connection *conn, void *data, size_t len,
|
682
|
710
|
/* Stop timer and update stored options */
|
683
|
711
|
stop_timer ( &dhcp->timer );
|
684
|
712
|
if ( dhcp->options )
|
685
|
|
- free_dhcp_options ( dhcp->options );
|
|
713
|
+ dhcpopt_put ( dhcp->options );
|
686
|
714
|
dhcp->options = options;
|
687
|
715
|
|
688
|
716
|
/* Transmit next packet, or terminate session */
|
689
|
717
|
if ( dhcp->state < DHCPACK ) {
|
690
|
718
|
dhcp_send_request ( dhcp );
|
691
|
719
|
} else {
|
692
|
|
- dhcp_done ( dhcp, 0 );
|
|
720
|
+ dhcp->register_options ( dhcp->options );
|
|
721
|
+ dhcp_finished ( dhcp, 0 );
|
693
|
722
|
}
|
694
|
723
|
return 0;
|
695
|
724
|
|
696
|
725
|
out_discard:
|
697
|
|
- free_dhcp_options ( options );
|
|
726
|
+ dhcpopt_put ( options );
|
698
|
727
|
return 0;
|
699
|
728
|
}
|
700
|
729
|
|
701
|
|
-/** DHCP UDP operations */
|
702
|
|
-static struct udp_operations dhcp_udp_operations = {
|
703
|
|
- .senddata = dhcp_senddata,
|
704
|
|
- .newdata = dhcp_newdata,
|
|
730
|
+/** DHCP data transfer interface operations */
|
|
731
|
+static struct xfer_interface_operations dhcp_xfer_operations = {
|
|
732
|
+ .close = ignore_xfer_close,
|
|
733
|
+ .vredirect = xfer_vopen,
|
|
734
|
+ .request = ignore_xfer_request,
|
|
735
|
+ .seek = ignore_xfer_seek,
|
|
736
|
+ .deliver_iob = xfer_deliver_as_raw,
|
|
737
|
+ .deliver_raw = dhcp_deliver_raw,
|
705
|
738
|
};
|
706
|
739
|
|
|
740
|
+/****************************************************************************
|
|
741
|
+ *
|
|
742
|
+ * Job control interface
|
|
743
|
+ *
|
|
744
|
+ */
|
|
745
|
+
|
707
|
746
|
/**
|
708
|
|
- * Initiate DHCP on a network interface
|
|
747
|
+ * Handle kill() event received via job control interface
|
709
|
748
|
*
|
710
|
|
- * @v dhcp DHCP session
|
711
|
|
- * @v parent Parent asynchronous operation
|
|
749
|
+ * @v job DHCP job control interface
|
|
750
|
+ */
|
|
751
|
+static void dhcp_job_kill ( struct job_interface *job ) {
|
|
752
|
+ struct dhcp_session *dhcp =
|
|
753
|
+ container_of ( job, struct dhcp_session, job );
|
|
754
|
+
|
|
755
|
+ /* Terminate DHCP session */
|
|
756
|
+ dhcp_finished ( dhcp, -ECANCELED );
|
|
757
|
+}
|
|
758
|
+
|
|
759
|
+/** DHCP job control interface operations */
|
|
760
|
+static struct job_interface_operations dhcp_job_operations = {
|
|
761
|
+ .start = ignore_job_start,
|
|
762
|
+ .done = ignore_job_done,
|
|
763
|
+ .kill = dhcp_job_kill,
|
|
764
|
+ .progress = ignore_job_progress,
|
|
765
|
+};
|
|
766
|
+
|
|
767
|
+/****************************************************************************
|
|
768
|
+ *
|
|
769
|
+ * Instantiator
|
|
770
|
+ *
|
|
771
|
+ */
|
|
772
|
+
|
|
773
|
+/**
|
|
774
|
+ * Start DHCP on a network device
|
|
775
|
+ *
|
|
776
|
+ * @v job Job control interface
|
|
777
|
+ * @v netdev Network device
|
|
778
|
+ * @v register_options DHCP option block registration routine
|
712
|
779
|
* @ret rc Return status code
|
713
|
780
|
*
|
714
|
|
- * If the DHCP operation completes successfully, the
|
715
|
|
- * dhcp_session::options field will be filled in with the resulting
|
716
|
|
- * options block. The caller takes responsibility for eventually
|
717
|
|
- * calling free_dhcp_options().
|
|
781
|
+ * Starts DHCP on the specified network device. If successful, the @c
|
|
782
|
+ * register_options() routine will be called with the acquired
|
|
783
|
+ * options.
|
718
|
784
|
*/
|
719
|
|
-int start_dhcp ( struct dhcp_session *dhcp, struct async *parent ) {
|
|
785
|
+int start_dhcp ( struct job_interface *job, struct net_device *netdev,
|
|
786
|
+ int ( * register_options ) ( struct dhcp_option_block * ) ) {
|
|
787
|
+ static struct sockaddr_in server = {
|
|
788
|
+ .sin_family = AF_INET,
|
|
789
|
+ .sin_addr.s_addr = INADDR_BROADCAST,
|
|
790
|
+ .sin_port = htons ( BOOTPS_PORT ),
|
|
791
|
+ };
|
|
792
|
+ static struct sockaddr_in client = {
|
|
793
|
+ .sin_family = AF_INET,
|
|
794
|
+ .sin_port = htons ( BOOTPC_PORT ),
|
|
795
|
+ };
|
|
796
|
+ struct dhcp_session *dhcp;
|
720
|
797
|
int rc;
|
721
|
798
|
|
722
|
|
- /* Initialise DHCP session */
|
723
|
|
- dhcp->udp.udp_op = &dhcp_udp_operations;
|
|
799
|
+ /* Allocate and initialise structure */
|
|
800
|
+ dhcp = malloc ( sizeof ( *dhcp ) );
|
|
801
|
+ if ( ! dhcp )
|
|
802
|
+ return -ENOMEM;
|
|
803
|
+ memset ( dhcp, 0, sizeof ( *dhcp ) );
|
|
804
|
+ dhcp->refcnt.free = dhcp_free;
|
|
805
|
+ job_init ( &dhcp->job, &dhcp_job_operations, &dhcp->refcnt );
|
|
806
|
+ xfer_init ( &dhcp->xfer, &dhcp_xfer_operations, &dhcp->refcnt );
|
|
807
|
+ dhcp->netdev = netdev_get ( netdev );
|
|
808
|
+ dhcp->register_options = register_options;
|
724
|
809
|
dhcp->timer.expired = dhcp_timer_expired;
|
725
|
|
- dhcp->state = DHCPDISCOVER;
|
726
|
810
|
|
727
|
|
- /* Bind to local port */
|
728
|
|
- if ( ( rc = udp_open ( &dhcp->udp, htons ( BOOTPC_PORT ) ) ) != 0 )
|
729
|
|
- return rc;
|
|
811
|
+ /* Instantiate child objects and attach to our interfaces */
|
|
812
|
+ if ( ( rc = xfer_open_socket ( &dhcp->xfer, SOCK_DGRAM,
|
|
813
|
+ ( struct sockaddr * ) &server,
|
|
814
|
+ ( struct sockaddr * ) &client ) ) != 0 )
|
|
815
|
+ goto err;
|
730
|
816
|
|
731
|
|
- /* Proof of concept: just send a single DHCPDISCOVER */
|
732
|
|
- dhcp_send_request ( dhcp );
|
|
817
|
+ /* Start timer to initiate initial DHCPREQUEST */
|
|
818
|
+ start_timer ( &dhcp->timer );
|
733
|
819
|
|
734
|
|
- async_init ( &dhcp->async, &default_async_operations, parent );
|
|
820
|
+ /* Attach parent interface, mortalise self, and return */
|
|
821
|
+ job_plug_plug ( &dhcp->job, job );
|
|
822
|
+ ref_put ( &dhcp->refcnt );
|
735
|
823
|
return 0;
|
736
|
|
-}
|
737
|
824
|
|
738
|
|
-
|
739
|
|
-#endif
|
|
825
|
+ err:
|
|
826
|
+ dhcp_finished ( dhcp, rc );
|
|
827
|
+ ref_put ( &dhcp->refcnt );
|
|
828
|
+ return rc;
|
|
829
|
+}
|