Browse Source

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

cycle.  :)
tags/v0.9.3
Michael Brown 18 years ago
parent
commit
7ca1bb0fbe
2 changed files with 170 additions and 43 deletions
  1. 11
    5
      src/include/gpxe/dhcp.h
  2. 159
    38
      src/net/udp/dhcp.c

+ 11
- 5
src/include/gpxe/dhcp.h View File

12
 #include <gpxe/in.h>
12
 #include <gpxe/in.h>
13
 #include <gpxe/udp.h>
13
 #include <gpxe/udp.h>
14
 #include <gpxe/async.h>
14
 #include <gpxe/async.h>
15
+#include <gpxe/retry.h>
15
 
16
 
16
 /** BOOTP/DHCP server port */
17
 /** BOOTP/DHCP server port */
17
 #define BOOTPS_PORT 67
18
 #define BOOTPS_PORT 67
407
 	/** UDP connection for this session */
408
 	/** UDP connection for this session */
408
 	struct udp_connection udp;
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
 	/** State of the session
419
 	/** State of the session
411
 	 *
420
 	 *
412
 	 * This is a value for the @c DHCP_MESSAGE_TYPE option
421
 	 * This is a value for the @c DHCP_MESSAGE_TYPE option
415
 	int state;
424
 	int state;
416
 	/** Asynchronous operation for this DHCP session */
425
 	/** Asynchronous operation for this DHCP session */
417
 	struct async_operation aop;
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
 extern unsigned long dhcp_num_option ( struct dhcp_option *option );
431
 extern unsigned long dhcp_num_option ( struct dhcp_option *option );

+ 159
- 38
src/net/udp/dhcp.c View File

109
 				    unsigned int tag, const void *data,
109
 				    unsigned int tag, const void *data,
110
 				    size_t len ) {
110
 				    size_t len ) {
111
 	struct dhcphdr *dhcphdr = dhcppkt->dhcphdr;
111
 	struct dhcphdr *dhcphdr = dhcppkt->dhcphdr;
112
-	struct dhcp_option_block *options;
112
+	struct dhcp_option_block *options = dhcppkt->options;
113
 	struct dhcp_option *option = NULL;
113
 	struct dhcp_option *option = NULL;
114
 
114
 
115
 	/* Special-case the magic options */
115
 	/* Special-case the magic options */
123
 	case DHCP_EB_SIADDR:
123
 	case DHCP_EB_SIADDR:
124
 		memcpy ( &dhcphdr->siaddr, data, sizeof ( dhcphdr->siaddr ) );
124
 		memcpy ( &dhcphdr->siaddr, data, sizeof ( dhcphdr->siaddr ) );
125
 		return 0;
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
 	default:
135
 	default:
127
 		/* Continue processing as normal */
136
 		/* Continue processing as normal */
128
 		break;
137
 		break;
129
 	}
138
 	}
130
 		
139
 		
131
 	/* Set option in first available options block */
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
 		option = set_dhcp_option ( options, tag, data, len );
142
 		option = set_dhcp_option ( options, tag, data, len );
135
 		if ( option )
143
 		if ( option )
136
 			break;
144
 			break;
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
  * @v dhcppkt		DHCP packet
190
  * @v dhcppkt		DHCP packet
150
  * @v options		DHCP option block, or NULL
191
  * @v options		DHCP option block, or NULL
158
  * @c options may specify a single options block, or be left as NULL
199
  * @c options may specify a single options block, or be left as NULL
159
  * in order to copy options from all registered options blocks.
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
 	unsigned int subtag;
205
 	unsigned int subtag;
165
 	unsigned int tag;
206
 	unsigned int tag;
166
-	struct dhcp_option *option;
167
 	int rc;
207
 	int rc;
168
 
208
 
169
 	for ( subtag = DHCP_MIN_OPTION; subtag <= DHCP_MAX_OPTION; subtag++ ) {
209
 	for ( subtag = DHCP_MIN_OPTION; subtag <= DHCP_MAX_OPTION; subtag++ ) {
172
 		case DHCP_EB_ENCAP:
212
 		case DHCP_EB_ENCAP:
173
 		case DHCP_VENDOR_ENCAP:
213
 		case DHCP_VENDOR_ENCAP:
174
 			/* Process encapsulated options field */
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
 				return rc;
218
 				return rc;
179
 			break;
219
 			break;
180
 		default:
220
 		default:
181
 			/* Copy option to reassembled packet */
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
 				return rc;
224
 				return rc;
189
 			break;
225
 			break;
190
 		};
226
 		};
194
 }
230
 }
195
 
231
 
196
 /**
232
 /**
197
- * Set options within DHCP packet
233
+ * Copy options into DHCP packet
198
  *
234
  *
199
  * @v dhcppkt		DHCP packet
235
  * @v dhcppkt		DHCP packet
200
  * @v options		DHCP option block, or NULL
236
  * @v options		DHCP option block, or NULL
207
  * @c options may specify a single options block, or be left as NULL
243
  * @c options may specify a single options block, or be left as NULL
208
  * in order to copy options from all registered options blocks.
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
  *
260
  *
225
  * Creates a DHCP packet in the specified buffer, and fills out a @c
261
  * Creates a DHCP packet in the specified buffer, and fills out a @c
226
  * dhcp_packet structure that can be passed to
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
 static int create_dhcp_packet ( struct dhcp_session *dhcp, uint8_t msgtype,
265
 static int create_dhcp_packet ( struct dhcp_session *dhcp, uint8_t msgtype,
230
 				void *data, size_t max_len,
266
 				void *data, size_t max_len,
232
 	struct dhcphdr *dhcphdr = data;
268
 	struct dhcphdr *dhcphdr = data;
233
 	static const uint8_t overloading = ( DHCP_OPTION_OVERLOAD_FILE |
269
 	static const uint8_t overloading = ( DHCP_OPTION_OVERLOAD_FILE |
234
 					     DHCP_OPTION_OVERLOAD_SNAME );
270
 					     DHCP_OPTION_OVERLOAD_SNAME );
271
+	int rc;
235
 
272
 
236
 	/* Sanity check */
273
 	/* Sanity check */
237
 	if ( max_len < sizeof ( *dhcphdr ) )
274
 	if ( max_len < sizeof ( *dhcphdr ) )
263
 			       sizeof ( overloading ) ) == NULL )
300
 			       sizeof ( overloading ) ) == NULL )
264
 		return -ENOSPC;
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
 	return 0;
309
 	return 0;
276
 }
310
 }
435
 	return container_of ( conn, struct dhcp_session, udp );
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
 /** Address for transmitting DHCP requests */
483
 /** Address for transmitting DHCP requests */
439
 static struct sockaddr sa_dhcp_server = {
484
 static struct sockaddr sa_dhcp_server = {
440
 	.sa_family = AF_INET,
485
 	.sa_family = AF_INET,
470
 	}
515
 	}
471
 
516
 
472
 	/* Copy in options common to all requests */
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
 		DBG ( "Could not set common DHCP options\n" );
520
 		DBG ( "Could not set common DHCP options\n" );
476
 		return;
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
 	/* Transmit the packet */
540
 	/* Transmit the packet */
480
 	if ( ( rc = udp_sendto ( conn, &sa_dhcp_server,
541
 	if ( ( rc = udp_sendto ( conn, &sa_dhcp_server,
481
 				 dhcppkt.dhcphdr, dhcppkt.len ) ) != 0 ) {
542
 				 dhcppkt.dhcphdr, dhcppkt.len ) ) != 0 ) {
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
  * Receive new data
576
  * Receive new data
489
  *
577
  *
496
 	struct dhcp_session *dhcp = udp_to_dhcp ( conn );
584
 	struct dhcp_session *dhcp = udp_to_dhcp ( conn );
497
 	struct dhcphdr *dhcphdr = data;
585
 	struct dhcphdr *dhcphdr = data;
498
 	struct dhcp_option_block *options;
586
 	struct dhcp_option_block *options;
587
+	unsigned int msgtype;
499
 
588
 
500
 	/* Check for matching transaction ID */
589
 	/* Check for matching transaction ID */
501
 	if ( dhcphdr->xid != dhcp->xid ) {
590
 	if ( dhcphdr->xid != dhcp->xid ) {
511
 		return;
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
 	free_dhcp_options ( options );
639
 	free_dhcp_options ( options );
521
 }
640
 }
522
 
641
 
535
 struct async_operation * start_dhcp ( struct dhcp_session *dhcp ) {
654
 struct async_operation * start_dhcp ( struct dhcp_session *dhcp ) {
536
 	int rc;
655
 	int rc;
537
 
656
 
657
+	/* Initialise DHCP session */
538
 	dhcp->udp.udp_op = &dhcp_udp_operations;
658
 	dhcp->udp.udp_op = &dhcp_udp_operations;
659
+	dhcp->timer.expired = dhcp_timer_expired;
539
 	dhcp->state = DHCPDISCOVER;
660
 	dhcp->state = DHCPDISCOVER;
540
 	/* Use least significant 32 bits of link-layer address as XID */
661
 	/* Use least significant 32 bits of link-layer address as XID */
541
 	memcpy ( &dhcp->xid, ( dhcp->netdev->ll_addr
662
 	memcpy ( &dhcp->xid, ( dhcp->netdev->ll_addr
549
 	}
670
 	}
550
 
671
 
551
 	/* Proof of concept: just send a single DHCPDISCOVER */
672
 	/* Proof of concept: just send a single DHCPDISCOVER */
552
-	udp_senddata ( &dhcp->udp );
673
+	dhcp_send_request ( dhcp );
553
 
674
 
554
  out:
675
  out:
555
 	return &dhcp->aop;
676
 	return &dhcp->aop;

Loading…
Cancel
Save