Explorar el Código

Now successfully negotiates the whole DHCPDISCOVER/OFFER/REQUEST/ACK

cycle.  :)
tags/v0.9.3
Michael Brown hace 18 años
padre
commit
7ca1bb0fbe
Se han modificado 2 ficheros con 170 adiciones y 43 borrados
  1. 11
    5
      src/include/gpxe/dhcp.h
  2. 159
    38
      src/net/udp/dhcp.c

+ 11
- 5
src/include/gpxe/dhcp.h Ver fichero

@@ -12,6 +12,7 @@
12 12
 #include <gpxe/in.h>
13 13
 #include <gpxe/udp.h>
14 14
 #include <gpxe/async.h>
15
+#include <gpxe/retry.h>
15 16
 
16 17
 /** BOOTP/DHCP server port */
17 18
 #define BOOTPS_PORT 67
@@ -407,6 +408,14 @@ struct dhcp_session {
407 408
 	/** UDP connection for this session */
408 409
 	struct udp_connection udp;
409 410
 
411
+	/** Network device being configured */
412
+	struct net_device *netdev;
413
+
414
+	/** Options obtained from server */
415
+	struct dhcp_option_block *options;
416
+
417
+	/** Transaction ID, in network-endian order */
418
+	uint32_t xid;
410 419
 	/** State of the session
411 420
 	 *
412 421
 	 * This is a value for the @c DHCP_MESSAGE_TYPE option
@@ -415,11 +424,8 @@ struct dhcp_session {
415 424
 	int state;
416 425
 	/** Asynchronous operation for this DHCP session */
417 426
 	struct async_operation aop;
418
-	
419
-	/** Network device being configured */
420
-	struct net_device *netdev;
421
-	/** Transaction ID, in network-endian order */
422
-	uint32_t xid;
427
+	/** Retransmission timer */
428
+	struct retry_timer timer;
423 429
 };
424 430
 
425 431
 extern unsigned long dhcp_num_option ( struct dhcp_option *option );

+ 159
- 38
src/net/udp/dhcp.c Ver fichero

@@ -109,7 +109,7 @@ static int set_dhcp_packet_option ( struct dhcp_packet *dhcppkt,
109 109
 				    unsigned int tag, const void *data,
110 110
 				    size_t len ) {
111 111
 	struct dhcphdr *dhcphdr = dhcppkt->dhcphdr;
112
-	struct dhcp_option_block *options;
112
+	struct dhcp_option_block *options = dhcppkt->options;
113 113
 	struct dhcp_option *option = NULL;
114 114
 
115 115
 	/* Special-case the magic options */
@@ -123,14 +123,22 @@ static int set_dhcp_packet_option ( struct dhcp_packet *dhcppkt,
123 123
 	case DHCP_EB_SIADDR:
124 124
 		memcpy ( &dhcphdr->siaddr, data, sizeof ( dhcphdr->siaddr ) );
125 125
 		return 0;
126
+	case DHCP_MESSAGE_TYPE:
127
+	case DHCP_REQUESTED_ADDRESS:
128
+		/* These options have to be within the main options
129
+		 * block.  This doesn't seem to be required by the
130
+		 * RFCs, but at least ISC dhcpd refuses to recognise
131
+		 * them otherwise.
132
+		 */
133
+		options = &dhcppkt->options[OPTS_MAIN];
134
+		break;
126 135
 	default:
127 136
 		/* Continue processing as normal */
128 137
 		break;
129 138
 	}
130 139
 		
131 140
 	/* Set option in first available options block */
132
-	for ( options = dhcppkt->options ;
133
-	      options < &dhcppkt->options[NUM_OPT_BLOCKS] ; options++ ) {
141
+	for ( ; options < &dhcppkt->options[NUM_OPT_BLOCKS] ; options++ ) {
134 142
 		option = set_dhcp_option ( options, tag, data, len );
135 143
 		if ( option )
136 144
 			break;
@@ -144,7 +152,40 @@ static int set_dhcp_packet_option ( struct dhcp_packet *dhcppkt,
144 152
 }
145 153
 
146 154
 /**
147
- * Set options within DHCP packet
155
+ * Copy option into DHCP packet
156
+ *
157
+ * @v dhcppkt		DHCP packet
158
+ * @v options		DHCP option block, or NULL
159
+ * @v tag		DHCP option tag to search for
160
+ * @v new_tag		DHCP option tag to use for copied option
161
+ * @ret rc		Return status code
162
+ *
163
+ * Copies a single option, if present, from the DHCP options block
164
+ * into a DHCP packet.  The tag for the option may be changed if
165
+ * desired; this is required by other parts of the DHCP code.
166
+ *
167
+ * @c options may specify a single options block, or be left as NULL
168
+ * in order to search for the option within all registered options
169
+ * blocks.
170
+ */
171
+static int copy_dhcp_packet_option ( struct dhcp_packet *dhcppkt,
172
+				     struct dhcp_option_block *options,
173
+				     unsigned int tag, unsigned int new_tag ) {
174
+	struct dhcp_option *option;
175
+	int rc;
176
+
177
+	option = find_dhcp_option ( options, tag );
178
+	if ( option ) {
179
+		if ( ( rc = set_dhcp_packet_option ( dhcppkt, new_tag,
180
+						     &option->data,
181
+						     option->len ) ) != 0 )
182
+			return rc;
183
+	}
184
+	return 0;
185
+}
186
+
187
+/**
188
+ * Copy options into DHCP packet
148 189
  *
149 190
  * @v dhcppkt		DHCP packet
150 191
  * @v options		DHCP option block, or NULL
@@ -158,12 +199,11 @@ static int set_dhcp_packet_option ( struct dhcp_packet *dhcppkt,
158 199
  * @c options may specify a single options block, or be left as NULL
159 200
  * in order to copy options from all registered options blocks.
160 201
  */
161
-static int set_dhcp_packet_encap_options ( struct dhcp_packet *dhcppkt,
162
-					   struct dhcp_option_block *options,
163
-					   unsigned int encapsulator ) {
202
+static int copy_dhcp_packet_encap_options ( struct dhcp_packet *dhcppkt,
203
+					    struct dhcp_option_block *options,
204
+					    unsigned int encapsulator ) {
164 205
 	unsigned int subtag;
165 206
 	unsigned int tag;
166
-	struct dhcp_option *option;
167 207
 	int rc;
168 208
 
169 209
 	for ( subtag = DHCP_MIN_OPTION; subtag <= DHCP_MAX_OPTION; subtag++ ) {
@@ -172,19 +212,15 @@ static int set_dhcp_packet_encap_options ( struct dhcp_packet *dhcppkt,
172 212
 		case DHCP_EB_ENCAP:
173 213
 		case DHCP_VENDOR_ENCAP:
174 214
 			/* Process encapsulated options field */
175
-			if ( ( rc = set_dhcp_packet_encap_options ( dhcppkt,
176
-								    options,
177
-								    tag )) !=0)
215
+			if ( ( rc = copy_dhcp_packet_encap_options ( dhcppkt,
216
+								     options,
217
+								     tag)) !=0)
178 218
 				return rc;
179 219
 			break;
180 220
 		default:
181 221
 			/* Copy option to reassembled packet */
182
-			option = find_dhcp_option ( options, tag );
183
-			if ( ! option )
184
-				break;
185
-			if ( ( rc = set_dhcp_packet_option ( dhcppkt, tag,
186
-							     &option->data,
187
-							     option->len)) !=0)
222
+			if ( ( rc = copy_dhcp_packet_option ( dhcppkt, options,
223
+							      tag, tag ) ) !=0)
188 224
 				return rc;
189 225
 			break;
190 226
 		};
@@ -194,7 +230,7 @@ static int set_dhcp_packet_encap_options ( struct dhcp_packet *dhcppkt,
194 230
 }
195 231
 
196 232
 /**
197
- * Set options within DHCP packet
233
+ * Copy options into DHCP packet
198 234
  *
199 235
  * @v dhcppkt		DHCP packet
200 236
  * @v options		DHCP option block, or NULL
@@ -207,9 +243,9 @@ static int set_dhcp_packet_encap_options ( struct dhcp_packet *dhcppkt,
207 243
  * @c options may specify a single options block, or be left as NULL
208 244
  * in order to copy options from all registered options blocks.
209 245
  */
210
-static int set_dhcp_packet_options ( struct dhcp_packet *dhcppkt,
211
-				     struct dhcp_option_block *options ) {
212
-	return set_dhcp_packet_encap_options ( dhcppkt, options, 0 );
246
+static int copy_dhcp_packet_options ( struct dhcp_packet *dhcppkt,
247
+				      struct dhcp_option_block *options ) {
248
+	return copy_dhcp_packet_encap_options ( dhcppkt, options, 0 );
213 249
 }
214 250
 
215 251
 /**
@@ -224,7 +260,7 @@ static int set_dhcp_packet_options ( struct dhcp_packet *dhcppkt,
224 260
  *
225 261
  * Creates a DHCP packet in the specified buffer, and fills out a @c
226 262
  * dhcp_packet structure that can be passed to
227
- * set_dhcp_packet_option() or set_dhcp_packet_options().
263
+ * set_dhcp_packet_option() or copy_dhcp_packet_options().
228 264
  */
229 265
 static int create_dhcp_packet ( struct dhcp_session *dhcp, uint8_t msgtype,
230 266
 				void *data, size_t max_len,
@@ -232,6 +268,7 @@ static int create_dhcp_packet ( struct dhcp_session *dhcp, uint8_t msgtype,
232 268
 	struct dhcphdr *dhcphdr = data;
233 269
 	static const uint8_t overloading = ( DHCP_OPTION_OVERLOAD_FILE |
234 270
 					     DHCP_OPTION_OVERLOAD_SNAME );
271
+	int rc;
235 272
 
236 273
 	/* Sanity check */
237 274
 	if ( max_len < sizeof ( *dhcphdr ) )
@@ -263,14 +300,11 @@ static int create_dhcp_packet ( struct dhcp_session *dhcp, uint8_t msgtype,
263 300
 			       sizeof ( overloading ) ) == NULL )
264 301
 		return -ENOSPC;
265 302
 
266
-	/* Set DHCP_MESSAGE_TYPE option within the main options block.
267
-	 * This doesn't seem to be required by the RFCs, but at least
268
-	 * ISC dhcpd and ethereal refuse to recognise it otherwise.
269
-	 */
270
-	if ( set_dhcp_option ( &dhcppkt->options[OPTS_MAIN],
271
-			       DHCP_MESSAGE_TYPE, &msgtype,
272
-			       sizeof ( msgtype ) ) == NULL )
273
-		return -ENOSPC;
303
+	/* Set DHCP_MESSAGE_TYPE option */
304
+	if ( ( rc = set_dhcp_packet_option ( dhcppkt, DHCP_MESSAGE_TYPE,
305
+					     &msgtype,
306
+					     sizeof ( msgtype ) ) ) != 0 )
307
+		return rc;
274 308
 
275 309
 	return 0;
276 310
 }
@@ -435,6 +469,17 @@ udp_to_dhcp ( struct udp_connection *conn ) {
435 469
 	return container_of ( conn, struct dhcp_session, udp );
436 470
 }
437 471
 
472
+/**
473
+ * Mark DHCP session as complete
474
+ *
475
+ * @v dhcp		DHCP session
476
+ * @v rc		Return status code
477
+ */
478
+static void dhcp_done ( struct dhcp_session *dhcp, int rc ) {
479
+	/* Mark async operation as complete */
480
+	async_done ( &dhcp->aop, rc );
481
+}
482
+
438 483
 /** Address for transmitting DHCP requests */
439 484
 static struct sockaddr sa_dhcp_server = {
440 485
 	.sa_family = AF_INET,
@@ -470,12 +515,28 @@ static void dhcp_senddata ( struct udp_connection *conn,
470 515
 	}
471 516
 
472 517
 	/* Copy in options common to all requests */
473
-	if ( ( rc = set_dhcp_packet_options ( &dhcppkt,
474
-					      &dhcp_request_options ) ) != 0 ){
518
+	if ( ( rc = copy_dhcp_packet_options ( &dhcppkt,
519
+					       &dhcp_request_options ) ) != 0){
475 520
 		DBG ( "Could not set common DHCP options\n" );
476 521
 		return;
477 522
 	}
478 523
 
524
+	/* Copy any required options from previous server repsonse */
525
+	if ( dhcp->options ) {
526
+		if ( ( rc = copy_dhcp_packet_option ( &dhcppkt, dhcp->options,
527
+					    DHCP_SERVER_IDENTIFIER,
528
+					    DHCP_SERVER_IDENTIFIER ) ) != 0 ) {
529
+			DBG ( "Could not set server identifier option\n" );
530
+			return;
531
+		}
532
+		if ( ( rc = copy_dhcp_packet_option ( &dhcppkt, dhcp->options,
533
+					    DHCP_EB_YIADDR,
534
+					    DHCP_REQUESTED_ADDRESS ) ) != 0 ) {
535
+			DBG ( "Could not set requested address option\n" );
536
+			return;
537
+		}
538
+	}
539
+
479 540
 	/* Transmit the packet */
480 541
 	if ( ( rc = udp_sendto ( conn, &sa_dhcp_server,
481 542
 				 dhcppkt.dhcphdr, dhcppkt.len ) ) != 0 ) {
@@ -484,6 +545,33 @@ static void dhcp_senddata ( struct udp_connection *conn,
484 545
 	}
485 546
 }
486 547
 
548
+/**
549
+ * Transmit DHCP request
550
+ *
551
+ * @v dhcp		DHCP session
552
+ */
553
+static void dhcp_send_request ( struct dhcp_session *dhcp ) {
554
+	start_timer ( &dhcp->timer );
555
+	udp_senddata ( &dhcp->udp );
556
+}
557
+
558
+/**
559
+ * Handle DHCP retry timer expiry
560
+ *
561
+ * @v timer		DHCP retry timer
562
+ * @v fail		Failure indicator
563
+ */
564
+static void dhcp_timer_expired ( struct retry_timer *timer, int fail ) {
565
+	struct dhcp_session *dhcp =
566
+		container_of ( timer, struct dhcp_session, timer );
567
+
568
+	if ( fail ) {
569
+		dhcp_done ( dhcp, -ETIMEDOUT );
570
+	} else {
571
+		dhcp_send_request ( dhcp );
572
+	}
573
+}
574
+
487 575
 /**
488 576
  * Receive new data
489 577
  *
@@ -496,6 +584,7 @@ static void dhcp_newdata ( struct udp_connection *conn,
496 584
 	struct dhcp_session *dhcp = udp_to_dhcp ( conn );
497 585
 	struct dhcphdr *dhcphdr = data;
498 586
 	struct dhcp_option_block *options;
587
+	unsigned int msgtype;
499 588
 
500 589
 	/* Check for matching transaction ID */
501 590
 	if ( dhcphdr->xid != dhcp->xid ) {
@@ -511,12 +600,42 @@ static void dhcp_newdata ( struct udp_connection *conn,
511 600
 		return;
512 601
 	}
513 602
 
514
-	DBG ( "Received %s\n",
515
-	      dhcp_msgtype_name ( find_dhcp_num_option ( options,
516
-							 DHCP_MESSAGE_TYPE )));
603
+	/* Determine message type */
604
+	msgtype = find_dhcp_num_option ( options, DHCP_MESSAGE_TYPE );
605
+	DBG ( "Received %s\n", dhcp_msgtype_name ( msgtype ) );
606
+
607
+	/* Handle DHCP reply */
608
+	switch ( dhcp->state ) {
609
+	case DHCPDISCOVER:
610
+		if ( msgtype != DHCPOFFER )
611
+			goto out_discard;
612
+		dhcp->state = DHCPREQUEST;
613
+		break;
614
+	case DHCPREQUEST:
615
+		if ( msgtype != DHCPACK )
616
+			goto out_discard;
617
+		dhcp->state = DHCPACK;
618
+		break;
619
+	default:
620
+		assert ( 0 );
621
+		goto out_discard;
622
+	}
623
+
624
+	/* Stop timer and update stored options */
625
+	stop_timer ( &dhcp->timer );
626
+	if ( dhcp->options )
627
+		free_dhcp_options ( dhcp->options );
628
+	dhcp->options = options;
517 629
 
518
-	/* Proof of concept: just dump out the parsed options */
519
-	hex_dump ( options->data, options->len );
630
+	/* Transmit next packet, or terminate session */
631
+	if ( dhcp->state < DHCPACK ) {
632
+		dhcp_send_request ( dhcp );
633
+	} else {
634
+		dhcp_done ( dhcp, 0 );
635
+	}
636
+	return;
637
+
638
+ out_discard:
520 639
 	free_dhcp_options ( options );
521 640
 }
522 641
 
@@ -535,7 +654,9 @@ static struct udp_operations dhcp_udp_operations = {
535 654
 struct async_operation * start_dhcp ( struct dhcp_session *dhcp ) {
536 655
 	int rc;
537 656
 
657
+	/* Initialise DHCP session */
538 658
 	dhcp->udp.udp_op = &dhcp_udp_operations;
659
+	dhcp->timer.expired = dhcp_timer_expired;
539 660
 	dhcp->state = DHCPDISCOVER;
540 661
 	/* Use least significant 32 bits of link-layer address as XID */
541 662
 	memcpy ( &dhcp->xid, ( dhcp->netdev->ll_addr
@@ -549,7 +670,7 @@ struct async_operation * start_dhcp ( struct dhcp_session *dhcp ) {
549 670
 	}
550 671
 
551 672
 	/* Proof of concept: just send a single DHCPDISCOVER */
552
-	udp_senddata ( &dhcp->udp );
673
+	dhcp_send_request ( dhcp );
553 674
 
554 675
  out:
555 676
 	return &dhcp->aop;

Loading…
Cancelar
Guardar