Преглед на файлове

[Settings] Migrate DHCP and NVO code to the new settings API (untested)

tags/v0.9.4
Michael Brown преди 16 години
родител
ревизия
8afb36c3bc

+ 4
- 4
src/arch/i386/image/nbi.c Целия файл

@@ -9,6 +9,7 @@
9 9
 #include <gpxe/init.h>
10 10
 #include <gpxe/netdevice.h>
11 11
 #include <gpxe/dhcp.h>
12
+#include <gpxe/dhcppkt.h>
12 13
 #include <gpxe/image.h>
13 14
 #include <gpxe/features.h>
14 15
 
@@ -400,10 +401,9 @@ static int nbi_prepare_dhcp ( struct image *image ) {
400 401
 		return -ENODEV;
401 402
 	}
402 403
 
403
-	if ( ( rc = create_dhcp_response ( boot_netdev, DHCPACK, NULL,
404
-					   basemem_packet,
405
-					   sizeof ( basemem_packet ),
406
-					   &dhcppkt ) ) != 0 ) {
404
+	if ( ( rc = create_dhcp_response ( &dhcppkt, boot_netdev, DHCPACK,
405
+					   NULL, basemem_packet,
406
+					   sizeof ( basemem_packet ) ) ) != 0){
407 407
 		DBGC ( image, "NBI %p failed to build DHCP packet\n", image );
408 408
 		return rc;
409 409
 	}

+ 129
- 87
src/core/nvo.c Целия файл

@@ -17,6 +17,7 @@
17 17
  */
18 18
 
19 19
 #include <stdint.h>
20
+#include <stdlib.h>
20 21
 #include <string.h>
21 22
 #include <errno.h>
22 23
 #include <gpxe/dhcp.h>
@@ -29,9 +30,6 @@
29 30
  *
30 31
  */
31 32
 
32
-/* FIXME: "Temporary hack" */
33
-struct nvo_block *ugly_nvo_hack = NULL;
34
-
35 33
 /**
36 34
  * Calculate checksum over non-volatile stored options
37 35
  *
@@ -39,7 +37,7 @@ struct nvo_block *ugly_nvo_hack = NULL;
39 37
  * @ret sum		Checksum
40 38
  */
41 39
 static unsigned int nvo_checksum ( struct nvo_block *nvo ) {
42
-	uint8_t *data = nvo->options->data;
40
+	uint8_t *data = nvo->data;
43 41
 	uint8_t sum = 0;
44 42
 	unsigned int i;
45 43
 
@@ -56,22 +54,22 @@ static unsigned int nvo_checksum ( struct nvo_block *nvo ) {
56 54
  * @ret rc		Return status code
57 55
  */
58 56
 static int nvo_load ( struct nvo_block *nvo ) {
59
-	void *data = nvo->options->data;
60
-	struct nvo_fragment *fragment;
57
+	void *data = nvo->data;
58
+	struct nvo_fragment *frag;
61 59
 	int rc;
62 60
 
63 61
 	/* Read data a fragment at a time */
64
-	for ( fragment = nvo->fragments ; fragment->len ; fragment++ ) {
65
-		if ( ( rc = nvs_read ( nvo->nvs, fragment->address,
66
-				       data, fragment->len ) ) != 0 ) {
67
-			DBG ( "NVO %p could not read %zd bytes at %#04x\n",
68
-			      nvo, fragment->len, fragment->address );
62
+	for ( frag = nvo->fragments ; frag->len ; frag++ ) {
63
+		if ( ( rc = nvs_read ( nvo->nvs, frag->address, data,
64
+				       frag->len ) ) != 0 ) {
65
+			DBGC ( nvo, "NVO %p could not read %zd bytes at "
66
+			       "%#04x\n", nvo, frag->len, frag->address );
69 67
 			return rc;
70 68
 		}
71
-		data += fragment->len;
69
+		data += frag->len;
72 70
 	}
73 71
 
74
-	DBG ( "NVO %p loaded from non-volatile storage\n", nvo );
72
+	DBGC ( nvo, "NVO %p loaded from non-volatile storage\n", nvo );
75 73
 	return 0;
76 74
 }
77 75
 
@@ -81,27 +79,27 @@ static int nvo_load ( struct nvo_block *nvo ) {
81 79
  * @v nvo		Non-volatile options block
82 80
  * @ret rc		Return status code
83 81
  */
84
-int nvo_save ( struct nvo_block *nvo ) {
85
-	void *data = nvo->options->data;
86
-	uint8_t *checksum = ( data + nvo->total_len - 1 );
87
-	struct nvo_fragment *fragment;
82
+static int nvo_save ( struct nvo_block *nvo ) {
83
+	void *data = nvo->data;
84
+	uint8_t *checksum = data;
85
+	struct nvo_fragment *frag;
88 86
 	int rc;
89 87
 
90 88
 	/* Recalculate checksum */
91 89
 	*checksum -= nvo_checksum ( nvo );
92 90
 
93 91
 	/* Write data a fragment at a time */
94
-	for ( fragment = nvo->fragments ; fragment->len ; fragment++ ) {
95
-		if ( ( rc = nvs_write ( nvo->nvs, fragment->address,
96
-					data, fragment->len ) ) != 0 ) {
97
-			DBG ( "NVO %p could not write %zd bytes at %#04x\n",
98
-			      nvo, fragment->len, fragment->address );
92
+	for ( frag = nvo->fragments ; frag->len ; frag++ ) {
93
+		if ( ( rc = nvs_write ( nvo->nvs, frag->address, data,
94
+					frag->len ) ) != 0 ) {
95
+			DBGC ( nvo, "NVO %p could not write %zd bytes at "
96
+			       "%#04x\n", nvo, frag->len, frag->address );
99 97
 			return rc;
100 98
 		}
101
-		data += fragment->len;
99
+		data += frag->len;
102 100
 	}
103 101
 
104
-	DBG ( "NVO %p saved to non-volatile storage\n", nvo );
102
+	DBGC ( nvo, "NVO %p saved to non-volatile storage\n", nvo );
105 103
 	return 0;
106 104
 }
107 105
 
@@ -114,88 +112,138 @@ int nvo_save ( struct nvo_block *nvo ) {
114 112
  * options block.  If the data is not valid, it is replaced with an
115 113
  * empty options block.
116 114
  */
117
-static void nvo_init_dhcp ( struct nvo_block *nvo ) {
118
-	struct dhcp_option_block *options = nvo->options;
119
-	struct dhcp_option *option;
115
+static void nvo_init_dhcpopts ( struct nvo_block *nvo ) {
116
+	uint8_t *options_data;
117
+	size_t options_len;
120 118
 
121 119
 	/* Steal one byte for the checksum */
122
-	options->max_len = ( nvo->total_len - 1 );
120
+	options_data = ( nvo->data + 1 );
121
+	options_len = ( nvo->total_len - 1 );
123 122
 
124
-	/* Verify checksum over whole block */
125
-	if ( nvo_checksum ( nvo ) != 0 ) {
126
-		DBG ( "NVO %p has bad checksum %02x; assuming empty\n",
127
-		      nvo, nvo_checksum ( nvo ) );
128
-		goto empty;
123
+	/* If checksum fails, or options data starts with a zero,
124
+	 * assume the whole block is invalid.  This should capture the
125
+	 * case of random initial contents.
126
+	 */
127
+	if ( ( nvo_checksum ( nvo ) != 0 ) || ( options_data[0] == 0 ) ) {
128
+		DBGC ( nvo, "NVO %p has checksum %02x and initial byte %02x; "
129
+		       "assuming empty\n", nvo, nvo_checksum ( nvo ),
130
+		       options_data[0] );
131
+		memset ( nvo->data, 0, nvo->total_len );
129 132
 	}
130 133
 
131
-	/* Check that we don't just have a block full of zeroes */
132
-	option = options->data;
133
-	if ( option->tag == DHCP_PAD ) {
134
-		DBG ( "NVO %p has bad start; assuming empty\n", nvo );
135
-		goto empty;
136
-	}
137
-	
138
-	/* Search for the DHCP_END tag */
139
-	options->len = options->max_len;
140
-	option = find_dhcp_option ( options, DHCP_END );
141
-	if ( ! option ) {
142
-		DBG ( "NVO %p has no end tag; assuming empty\n", nvo );
143
-		goto empty;
134
+	dhcpopt_init ( &nvo->dhcpopts, options_data, options_len );
135
+}
136
+
137
+/**
138
+ * Store value of NVO setting
139
+ *
140
+ * @v settings		Settings block
141
+ * @v tag		Setting tag number
142
+ * @v data		Setting data, or NULL to clear setting
143
+ * @v len		Length of setting data
144
+ * @ret rc		Return status code
145
+ */
146
+static int nvo_store ( struct settings *settings, unsigned int tag,
147
+		       const void *data, size_t len ) {
148
+	struct nvo_block *nvo =
149
+		container_of ( settings, struct nvo_block, settings );
150
+	int rc;
151
+
152
+	/* Update stored options */
153
+	if ( ( rc = dhcpopt_store ( &nvo->dhcpopts, tag, data, len ) ) != 0 ) {
154
+		DBGC ( nvo, "NVO %p could not store %zd bytes: %s\n",
155
+		       nvo, len, strerror ( rc ) );
156
+		return rc;
144 157
 	}
145 158
 
146
-	/* Set correct length of DHCP options */
147
-	options->len = ( ( void * ) option - options->data + 1 );
148
-	DBG ( "NVO %p contains %zd bytes of options (maximum %zd)\n",
149
-	      nvo, options->len, options->max_len );
150
-	return;
151
-
152
- empty:
153
-	/* No options found; initialise an empty options block */
154
-	option = options->data;
155
-	option->tag = DHCP_END;
156
-	options->len = 1;
157
-	return;
159
+	/* Save updated options to NVS */
160
+	if ( ( rc = nvo_save ( nvo ) ) != 0 )
161
+		return rc;
162
+
163
+	return 0;
164
+}
165
+
166
+/**
167
+ * Fetch value of NVO setting
168
+ *
169
+ * @v settings		Settings block
170
+ * @v tag		Setting tag number
171
+ * @v data		Buffer to fill with setting data
172
+ * @v len		Length of buffer
173
+ * @ret len		Length of setting data, or negative error
174
+ *
175
+ * The actual length of the setting will be returned even if
176
+ * the buffer was too small.
177
+ */
178
+static int nvo_fetch ( struct settings *settings, unsigned int tag,
179
+		       void *data, size_t len ) {
180
+	struct nvo_block *nvo =
181
+		container_of ( settings, struct nvo_block, settings );
182
+
183
+	return dhcpopt_fetch ( &nvo->dhcpopts, tag, data, len );
184
+}
185
+
186
+/** NVO settings operations */
187
+static struct settings_operations nvo_settings_operations = {
188
+	.store = nvo_store,
189
+	.fetch = nvo_fetch,
190
+};
191
+
192
+/**
193
+ * Initialise non-volatile stored options
194
+ *
195
+ * @v nvo		Non-volatile options block
196
+ * @v nvs		Underlying non-volatile storage device
197
+ * @v fragments		List of option-containing fragments
198
+ * @v refcnt		Containing object reference counter, or NULL
199
+ */
200
+void nvo_init ( struct nvo_block *nvo, struct nvs_device *nvs,
201
+		struct nvo_fragment *fragments, struct refcnt *refcnt ) {
202
+	nvo->nvs = nvs;
203
+	nvo->fragments = fragments;
204
+	settings_init ( &nvo->settings, &nvo_settings_operations, refcnt,
205
+			"nvo" );
158 206
 }
159 207
 
160 208
 /**
161 209
  * Register non-volatile stored options
162 210
  *
163 211
  * @v nvo		Non-volatile options block
212
+ * @v parent		Parent settings block, or NULL
164 213
  * @ret rc		Return status code
165 214
  */
166
-int nvo_register ( struct nvo_block *nvo ) {
215
+int register_nvo ( struct nvo_block *nvo, struct settings *parent ) {
167 216
 	struct nvo_fragment *fragment = nvo->fragments;
168 217
 	int rc;
169 218
 
170 219
 	/* Calculate total length of all fragments */
171
-	nvo->total_len = 0;
172
-	for ( fragment = nvo->fragments ; fragment->len ; fragment++ ) {
220
+	for ( fragment = nvo->fragments ; fragment->len ; fragment++ )
173 221
 		nvo->total_len += fragment->len;
174
-	}
175 222
 
176 223
 	/* Allocate memory for options and read in from NVS */
177
-	nvo->options = alloc_dhcp_options ( nvo->total_len );
178
-	if ( ! nvo->options ) {
179
-		DBG ( "NVO %p could not allocate %zd bytes\n",
180
-		      nvo, nvo->total_len );
224
+	nvo->data = malloc ( nvo->total_len );
225
+	if ( ! nvo->data ) {
226
+		DBGC ( nvo, "NVO %p could not allocate %zd bytes\n",
227
+		       nvo, nvo->total_len );
181 228
 		rc = -ENOMEM;
182
-		goto err;
229
+		goto err_malloc;
183 230
 	}
184 231
 	if ( ( rc = nvo_load ( nvo ) ) != 0 )
185
-		goto err;
232
+		goto err_load;
186 233
 
187 234
 	/* Verify and register options */
188
-	nvo_init_dhcp ( nvo );
189
-	register_dhcp_options ( nvo->options );
190
-
191
-	ugly_nvo_hack = nvo;
235
+	nvo_init_dhcpopts ( nvo );
236
+	if ( ( rc = register_settings ( &nvo->settings, parent ) ) != 0 )
237
+		goto err_register;
192 238
 
193
-	DBG ( "NVO %p registered\n", nvo );
239
+	DBGC ( nvo, "NVO %p registered\n", nvo );
194 240
 	return 0;
195 241
 	
196
- err:
197
-	dhcpopt_put ( nvo->options );
198
-	nvo->options = NULL;
242
+ err_register:
243
+ err_load:
244
+	free ( nvo->data );
245
+	nvo->data = NULL;
246
+ err_malloc:
199 247
 	return rc;
200 248
 }
201 249
 
@@ -204,15 +252,9 @@ int nvo_register ( struct nvo_block *nvo ) {
204 252
  *
205 253
  * @v nvo		Non-volatile options block
206 254
  */
207
-void nvo_unregister ( struct nvo_block *nvo ) {
208
-
209
-	if ( nvo->options ) {
210
-		unregister_dhcp_options ( nvo->options );
211
-		dhcpopt_put ( nvo->options );
212
-		nvo->options = NULL;
213
-	}
214
-
215
-	DBG ( "NVO %p unregistered\n", nvo );
216
-
217
-	ugly_nvo_hack = NULL;
255
+void unregister_nvo ( struct nvo_block *nvo ) {
256
+	unregister_settings ( &nvo->settings );
257
+	free ( nvo->data );
258
+	nvo->data = NULL;
259
+	DBGC ( nvo, "NVO %p unregistered\n", nvo );
218 260
 }

+ 98
- 3
src/core/settings.c Целия файл

@@ -213,6 +213,7 @@ void unregister_settings ( struct settings *settings ) {
213 213
 	/* Remove from list of settings */
214 214
 	ref_put ( settings->refcnt );
215 215
 	ref_put ( settings->parent->refcnt );
216
+	settings->parent = NULL;
216 217
 	list_del ( &settings->siblings );
217 218
 	DBGC ( settings, "Settings %p unregistered\n", settings );
218 219
 
@@ -280,8 +281,13 @@ struct settings * find_settings ( const char *name ) {
280 281
  */
281 282
 int store_setting ( struct settings *settings, unsigned int tag,
282 283
 		    const void *data, size_t len ) {
284
+	struct settings *parent;
283 285
 	int rc;
284 286
 
287
+	/* Sanity check */
288
+	if ( ! settings )
289
+		return -ENODEV;
290
+
285 291
 	/* Store setting */
286 292
 	if ( ( rc = settings->op->store ( settings, tag, data, len ) ) != 0 )
287 293
 		return rc;
@@ -290,9 +296,16 @@ int store_setting ( struct settings *settings, unsigned int tag,
290 296
 	if ( tag == DHCP_EB_PRIORITY )
291 297
 		reprioritise_settings ( settings );
292 298
 
293
-	/* Apply potentially-updated setting */
294
-	if ( ( rc = apply_settings() ) != 0 )
295
-		return rc;
299
+	/* If these settings are registered, apply potentially-updated
300
+	 * settings
301
+	 */
302
+	for ( parent = settings->parent ; parent ; parent = parent->parent ) {
303
+		if ( parent == &settings_root ) {
304
+			if ( ( rc = apply_settings() ) != 0 )
305
+				return rc;
306
+			break;
307
+		}
308
+	}
296 309
 
297 310
 	return 0;
298 311
 }
@@ -468,6 +481,88 @@ unsigned long fetch_uintz_setting ( struct settings *settings,
468 481
 	return value;
469 482
 }
470 483
 
484
+/**
485
+ * Copy setting
486
+ *
487
+ * @v dest		Destination settings block
488
+ * @v dest_tag		Destination setting tag number
489
+ * @v source		Source settings block
490
+ * @v source_tag	Source setting tag number
491
+ * @ret rc		Return status code
492
+ */
493
+int copy_setting ( struct settings *dest, unsigned int dest_tag,
494
+		   struct settings *source, unsigned int source_tag ) {
495
+	int len;
496
+	int check_len;
497
+	int rc;
498
+
499
+	len = fetch_setting_len ( source, source_tag );
500
+	if ( len < 0 )
501
+		return len;
502
+
503
+	{
504
+		char buf[len];
505
+
506
+		check_len = fetch_setting ( source, source_tag, buf,
507
+					    sizeof ( buf ) );
508
+		assert ( check_len == len );
509
+
510
+		if ( ( rc = store_setting ( dest, dest_tag, buf,
511
+					    sizeof ( buf ) ) ) != 0 )
512
+			return rc;
513
+	}
514
+
515
+	return 0;
516
+}
517
+
518
+/**
519
+ * Copy settings
520
+ *
521
+ * @v dest		Destination settings block
522
+ * @v source		Source settings block
523
+ * @v encapsulator	Encapsulating setting tag number, or zero
524
+ * @ret rc		Return status code
525
+ */
526
+static int copy_encap_settings ( struct settings *dest,
527
+				 struct settings *source,
528
+				 unsigned int encapsulator ) {
529
+	unsigned int subtag;
530
+	unsigned int tag;
531
+	int rc;
532
+
533
+	for ( subtag = DHCP_MIN_OPTION; subtag <= DHCP_MAX_OPTION; subtag++ ) {
534
+		tag = DHCP_ENCAP_OPT ( encapsulator, subtag );
535
+		switch ( tag ) {
536
+		case DHCP_EB_ENCAP:
537
+		case DHCP_VENDOR_ENCAP:
538
+			/* Process encapsulated options field */
539
+			if ( ( rc = copy_encap_settings ( dest, source,
540
+							  tag ) ) != 0 )
541
+				return rc;
542
+			break;
543
+		default:
544
+			/* Copy option to reassembled packet */
545
+			if ( ( rc = copy_setting ( dest, tag, source,
546
+						   tag ) ) != 0 )
547
+				return rc;
548
+			break;
549
+		}
550
+	}
551
+
552
+	return 0;
553
+}
554
+
555
+/**
556
+ * Copy settings
557
+ *
558
+ * @v dest		Destination settings block
559
+ * @v source		Source settings block
560
+ * @ret rc		Return status code
561
+ */
562
+int copy_settings ( struct settings *dest, struct settings *source ) {
563
+	return copy_encap_settings ( dest, source, 0 );
564
+}
565
+
471 566
 /******************************************************************************
472 567
  *
473 568
  * Named and typed setting routines

+ 3
- 3
src/drivers/net/etherfabric.c Целия файл

@@ -3008,9 +3008,9 @@ static int falcon_init_nic ( struct efab_nic *efab ) {
3008 3008
 
3009 3009
 	/* Register non-volatile storage */
3010 3010
 	if ( efab->has_eeprom ) {
3011
-		efab->nvo.nvs = &efab->falcon_eeprom.nvs;
3012
-		efab->nvo.fragments = falcon_eeprom_fragments;
3013
-		if ( nvo_register ( &efab->nvo ) != 0 )
3011
+		nvo_init ( &efab->nvo, &efab->falcon_eeprom.nvs,
3012
+			   falcon_eeprom_fragments, NULL /* hack */ );
3013
+		if ( register_nvo ( &efab->nvo, NULL /* hack */ ) != 0 )
3014 3014
 			return 0;
3015 3015
 	}
3016 3016
 

+ 18
- 14
src/drivers/net/rtl8139.c Целия файл

@@ -261,9 +261,10 @@ static struct nvo_fragment rtl_nvo_fragments[] = {
261 261
 /**
262 262
  * Set up for EEPROM access
263 263
  *
264
- * @v rtl		RTL8139 NIC
264
+ * @v netdev		Net device
265 265
  */
266
-static void rtl_init_eeprom ( struct rtl8139_nic *rtl ) {
266
+static void rtl_init_eeprom ( struct net_device *netdev ) {
267
+	struct rtl8139_nic *rtl = netdev->priv;
267 268
 	int ee9356;
268 269
 	int vpd;
269 270
 
@@ -288,19 +289,20 @@ static void rtl_init_eeprom ( struct rtl8139_nic *rtl ) {
288 289
 	if ( vpd ) {
289 290
 		DBG ( "EEPROM in use for VPD; cannot use for options\n" );
290 291
 	} else {
291
-		rtl->nvo.nvs = &rtl->eeprom.nvs;
292
-		rtl->nvo.fragments = rtl_nvo_fragments;
292
+		nvo_init ( &rtl->nvo, &rtl->eeprom.nvs, rtl_nvo_fragments,
293
+			   &netdev->refcnt );
293 294
 	}
294 295
 }
295 296
 
296 297
 /**
297 298
  * Reset NIC
298 299
  *
299
- * @v rtl		RTL8139 NIC
300
+ * @v netdev		Net device
300 301
  *
301 302
  * Issues a hardware reset and waits for the reset to complete.
302 303
  */
303
-static void rtl_reset ( struct rtl8139_nic *rtl ) {
304
+static void rtl_reset ( struct net_device *netdev ) {
305
+	struct rtl8139_nic *rtl = netdev->priv;
304 306
 
305 307
 	/* Reset chip */
306 308
 	outb ( CmdReset, rtl->ioaddr + ChipCmd );
@@ -352,7 +354,7 @@ static void rtl_close ( struct net_device *netdev ) {
352 354
 	struct rtl8139_nic *rtl = netdev->priv;
353 355
 
354 356
 	/* Reset the hardware to disable everything in one go */
355
-	rtl_reset ( rtl );
357
+	rtl_reset ( netdev );
356 358
 
357 359
 	/* Free RX ring */
358 360
 	free ( rtl->rx.ring );
@@ -366,7 +368,8 @@ static void rtl_close ( struct net_device *netdev ) {
366 368
  * @v iobuf	I/O buffer
367 369
  * @ret rc	Return status code
368 370
  */
369
-static int rtl_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) {
371
+static int rtl_transmit ( struct net_device *netdev,
372
+			  struct io_buffer *iobuf ) {
370 373
 	struct rtl8139_nic *rtl = netdev->priv;
371 374
 
372 375
 	/* Check for space in TX ring */
@@ -512,8 +515,8 @@ static int rtl_probe ( struct pci_device *pci,
512 515
 	adjust_pci_device ( pci );
513 516
 
514 517
 	/* Reset the NIC, set up EEPROM access and read MAC address */
515
-	rtl_reset ( rtl );
516
-	rtl_init_eeprom ( rtl );
518
+	rtl_reset ( netdev );
519
+	rtl_init_eeprom ( netdev );
517 520
 	nvs_read ( &rtl->eeprom.nvs, EE_MAC, netdev->ll_addr, ETH_ALEN );
518 521
 	
519 522
 	/* Register network device */
@@ -522,7 +525,8 @@ static int rtl_probe ( struct pci_device *pci,
522 525
 
523 526
 	/* Register non-volatile storage */
524 527
 	if ( rtl->nvo.nvs ) {
525
-		if ( ( rc = nvo_register ( &rtl->nvo ) ) != 0 )
528
+		if ( ( rc = register_nvo ( &rtl->nvo,
529
+					   netdev_settings ( netdev ) ) ) != 0)
526 530
 			goto err_register_nvo;
527 531
 	}
528 532
 
@@ -531,7 +535,7 @@ static int rtl_probe ( struct pci_device *pci,
531 535
  err_register_nvo:
532 536
 	unregister_netdev ( netdev );
533 537
  err_register_netdev:
534
-	rtl_reset ( rtl );
538
+	rtl_reset ( netdev );
535 539
 	netdev_nullify ( netdev );
536 540
 	netdev_put ( netdev );
537 541
 	return rc;
@@ -547,9 +551,9 @@ static void rtl_remove ( struct pci_device *pci ) {
547 551
 	struct rtl8139_nic *rtl = netdev->priv;
548 552
 
549 553
 	if ( rtl->nvo.nvs )
550
-		nvo_unregister ( &rtl->nvo );
554
+		unregister_nvo ( &rtl->nvo );
551 555
 	unregister_netdev ( netdev );
552
-	rtl_reset ( rtl );
556
+	rtl_reset ( netdev );
553 557
 	netdev_nullify ( netdev );
554 558
 	netdev_put ( netdev );
555 559
 }

+ 17
- 106
src/include/gpxe/dhcp.h Целия файл

@@ -8,13 +8,15 @@
8 8
  */
9 9
 
10 10
 #include <stdint.h>
11
-#include <gpxe/list.h>
12 11
 #include <gpxe/in.h>
12
+#include <gpxe/list.h>
13 13
 #include <gpxe/refcnt.h>
14 14
 #include <gpxe/tables.h>
15 15
 
16 16
 struct net_device;
17 17
 struct job_interface;
18
+struct dhcp_packet;
19
+struct settings;
18 20
 
19 21
 /** BOOTP/DHCP server port */
20 22
 #define BOOTPS_PORT 67
@@ -294,7 +296,7 @@ struct job_interface;
294 296
 
295 297
 /** Construct a word-valued DHCP option */
296 298
 #define DHCP_WORD( value ) DHCP_OPTION ( ( ( (value) >> 8 ) & 0xff ),   \
297
-					 ( ( (value) >> 0  ) & 0xff ) )
299
+					 ( ( (value) >> 0 ) & 0xff ) )
298 300
 
299 301
 /** Construct a dword-valued DHCP option */
300 302
 #define DHCP_DWORD( value ) DHCP_OPTION ( ( ( (value) >> 24 ) & 0xff ), \
@@ -327,21 +329,8 @@ struct dhcp_option {
327 329
 	 * byte in length.
328 330
 	 */
329 331
 	uint8_t len;
330
-	/** Option data
331
-	 *
332
-	 * Interpretation of the content is entirely dependent upon
333
-	 * the tag.  For fields containing a multi-byte integer, the
334
-	 * field is defined to be in network-endian order (unless you
335
-	 * are Intel and feel like violating the spec for fun).
336
-	 */
337
-	union {
338
-		uint8_t byte;
339
-		uint16_t word;
340
-		uint32_t dword;
341
-		struct in_addr in;
342
-		uint8_t bytes[0];
343
-		char string[0];
344
-	} data;
332
+	/** Option data */
333
+	uint8_t data[0];
345 334
 } __attribute__ (( packed ));
346 335
 
347 336
 /**
@@ -355,27 +344,6 @@ struct dhcp_option {
355 344
 /** Maximum length for a single DHCP option */
356 345
 #define DHCP_MAX_LEN 0xff
357 346
 
358
-/** A DHCP options block */
359
-struct dhcp_option_block {
360
-	/** Reference counter */
361
-	struct refcnt refcnt;
362
-	/** List of option blocks */
363
-	struct list_head list;
364
-	/** Option block raw data */
365
-	void *data;
366
-	/** Option block length */
367
-	size_t len;
368
-	/** Option block maximum length */
369
-	size_t max_len;
370
-	/** Block priority
371
-	 *
372
-	 * This is determined at the time of the call to
373
-	 * register_options() by searching for the @c DHCP_EB_PRIORITY
374
-	 * option.
375
-	 */
376
-	signed int priority;
377
-};
378
-
379 347
 /**
380 348
  * A DHCP header
381 349
  *
@@ -448,7 +416,7 @@ struct dhcphdr {
448 416
 	 * length (for the sake of sanity) is 1, to allow for a single
449 417
 	 * @c DHCP_END tag.
450 418
 	 */
451
-	uint8_t options[1];
419
+	uint8_t options[0];
452 420
 };
453 421
 
454 422
 /** Opcode for a request from client to server */
@@ -474,74 +442,17 @@ struct dhcphdr {
474 442
  */
475 443
 #define DHCP_MIN_LEN 552
476 444
 
477
-/**
478
- * A DHCP packet
479
- *
480
- */
481
-struct dhcp_packet {
482
-	/** The DHCP packet contents */
483
-	struct dhcphdr *dhcphdr;
484
-	/** Maximum length of the DHCP packet buffer */
485
-	size_t max_len;
486
-	/** Used length of the DHCP packet buffer */
487
-	size_t len;
488
-	/** DHCP options */
489
-	struct dhcp_option_block options;
490
-};
491
-
492
-/**
493
- * Get reference to DHCP options block
494
- *
495
- * @v options		DHCP options block
496
- * @ret options		DHCP options block
497
- */
498
-static inline __attribute__ (( always_inline )) struct dhcp_option_block *
499
-dhcpopt_get ( struct dhcp_option_block *options ) {
500
-	ref_get ( &options->refcnt );
501
-	return options;
502
-}
503
-
504
-/**
505
- * Drop reference to DHCP options block
506
- *
507
- * @v options		DHCP options block, or NULL
508
- */
509
-static inline __attribute__ (( always_inline )) void
510
-dhcpopt_put ( struct dhcp_option_block *options ) {
511
-	ref_put ( &options->refcnt );
512
-}
513
-
514 445
 /** Maximum time that we will wait for ProxyDHCP offers */
515 446
 #define PROXYDHCP_WAIT_TIME ( TICKS_PER_SEC * 1 )
516 447
 
517
-extern struct list_head dhcp_option_blocks;
518
-
519
-extern unsigned long dhcp_num_option ( struct dhcp_option *option );
520
-extern struct dhcp_option *
521
-find_dhcp_option ( struct dhcp_option_block *options, unsigned int tag );
522
-extern void register_dhcp_options ( struct dhcp_option_block *options );
523
-extern void unregister_dhcp_options ( struct dhcp_option_block *options );
524
-extern void init_dhcp_options ( struct dhcp_option_block *options,
525
-				void *data, size_t max_len );
526
-extern struct dhcp_option_block * __malloc alloc_dhcp_options ( size_t max_len );
527
-extern struct dhcp_option *
528
-set_dhcp_option ( struct dhcp_option_block *options, unsigned int tag,
529
-		  const void *data, size_t len );
530
-extern unsigned long find_dhcp_num_option ( struct dhcp_option_block *options,
531
-					    unsigned int tag );
532
-extern void delete_dhcp_option ( struct dhcp_option_block *options,
533
-				 unsigned int tag );
534
-
535
-extern int create_dhcp_request ( struct net_device *netdev, int msgtype,
536
-				 struct dhcp_option_block *options,
537
-				 void *data, size_t max_len,
538
-				 struct dhcp_packet *dhcppkt );
539
-extern int create_dhcp_response ( struct net_device *netdev, int msgtype,
540
-				  struct dhcp_option_block *options,
541
-				  void *data, size_t max_len,
542
-				  struct dhcp_packet *dhcppkt );
543
-
544
-extern int start_dhcp ( struct job_interface *job, struct net_device *netdev,
545
-			int (*register_options) ( struct net_device *,
546
-						  struct dhcp_option_block * ));
448
+extern int create_dhcp_request ( struct dhcp_packet *dhcppkt,
449
+				 struct net_device *netdev, int msgtype,
450
+				 struct settings *offer_settings,
451
+				 void *data, size_t max_len );
452
+extern int create_dhcp_response ( struct dhcp_packet *dhcppkt,
453
+				  struct net_device *netdev, int msgtype,
454
+				  struct settings *settings,
455
+				  void *data, size_t max_len );
456
+extern int start_dhcp ( struct job_interface *job, struct net_device *netdev );
457
+
547 458
 #endif /* _GPXE_DHCP_H */

+ 32
- 0
src/include/gpxe/dhcpopts.h Целия файл

@@ -0,0 +1,32 @@
1
+#ifndef _GPXE_DHCPOPTS_H
2
+#define _GPXE_DHCPOPTS_H
3
+
4
+/** @file
5
+ *
6
+ * DHCP options
7
+ *
8
+ */
9
+
10
+#include <gpxe/dhcp.h>
11
+
12
+/** A DHCP options block */
13
+struct dhcp_options {
14
+	/** Option block raw data */
15
+	void *data;
16
+	/** Option block length */
17
+	size_t len;
18
+	/** Option block maximum length */
19
+	size_t max_len;
20
+};
21
+
22
+extern int dhcpopt_store ( struct dhcp_options *options, unsigned int tag,
23
+			   const void *data, size_t len );
24
+extern int dhcpopt_extensible_store ( struct dhcp_options *options,
25
+				      unsigned int tag,
26
+				      const void *data, size_t len );
27
+extern int dhcpopt_fetch ( struct dhcp_options *options, unsigned int tag,
28
+			   void *data, size_t len );
29
+extern void dhcpopt_init ( struct dhcp_options *options,
30
+			   void *data, size_t max_len );
31
+
32
+#endif /* _GPXE_DHCPOPTS_H */

+ 34
- 0
src/include/gpxe/dhcppkt.h Целия файл

@@ -0,0 +1,34 @@
1
+#ifndef _GPXE_DHCPPKT_H
2
+#define _GPXE_DHCPPKT_H
3
+
4
+/** @file
5
+ *
6
+ * DHCP packets
7
+ *
8
+ */
9
+
10
+#include <gpxe/dhcp.h>
11
+#include <gpxe/dhcpopts.h>
12
+#include <gpxe/settings.h>
13
+
14
+/**
15
+ * A DHCP packet
16
+ *
17
+ */
18
+struct dhcp_packet {
19
+	/** Settings block */
20
+	struct settings settings;
21
+	/** The DHCP packet contents */
22
+	struct dhcphdr *dhcphdr;
23
+	/** Maximum length of the DHCP packet buffer */
24
+	size_t max_len;
25
+	/** Used length of the DHCP packet buffer */
26
+	size_t len;
27
+	/** DHCP option blocks */
28
+	struct dhcp_options options;
29
+};
30
+
31
+extern void dhcppkt_init ( struct dhcp_packet *dhcppkt, struct refcnt *refcnt,
32
+			   void *data, size_t len );
33
+
34
+#endif /* _GPXE_DHCPPKT_H */

+ 1
- 0
src/include/gpxe/errfile.h Целия файл

@@ -130,6 +130,7 @@
130 130
 #define ERRFILE_tftp			( ERRFILE_NET | 0x00120000 )
131 131
 #define ERRFILE_infiniband		( ERRFILE_NET | 0x00130000 )
132 132
 #define ERRFILE_netdev_settings		( ERRFILE_NET | 0x00140000 )
133
+#define ERRFILE_dhcppkt			( ERRFILE_NET | 0x00150000 )
133 134
 
134 135
 #define ERRFILE_image		      ( ERRFILE_IMAGE | 0x00000000 )
135 136
 #define ERRFILE_elf		      ( ERRFILE_IMAGE | 0x00010000 )

+ 13
- 9
src/include/gpxe/nvo.h Целия файл

@@ -8,9 +8,11 @@
8 8
  */
9 9
 
10 10
 #include <stdint.h>
11
+#include <gpxe/dhcpopts.h>
12
+#include <gpxe/settings.h>
11 13
 
12 14
 struct nvs_device;
13
-struct dhcp_option_block;
15
+struct refcnt;
14 16
 
15 17
 /**
16 18
  * A fragment of a non-volatile storage device used for stored options
@@ -26,6 +28,8 @@ struct nvo_fragment {
26 28
  * A block of non-volatile stored options
27 29
  */
28 30
 struct nvo_block {
31
+	/** Settings block */
32
+	struct settings settings;
29 33
 	/** Underlying non-volatile storage device */
30 34
 	struct nvs_device *nvs;
31 35
 	/** List of option-containing fragments
@@ -33,17 +37,17 @@ struct nvo_block {
33 37
 	 * The list is terminated by a fragment with a length of zero.
34 38
 	 */
35 39
 	struct nvo_fragment *fragments;
36
-	/** Total length of all fragments
37
-	 *
38
-	 * This field is filled in by nvo_register().
39
-	 */
40
+	/** Total length of option-containing fragments */
40 41
 	size_t total_len;
42
+	/** Option-containing data */
43
+	void *data;
41 44
 	/** DHCP options block */
42
-	struct dhcp_option_block *options;
45
+	struct dhcp_options dhcpopts;
43 46
 };
44 47
 
45
-extern int nvo_register ( struct nvo_block *nvo );
46
-extern int nvo_save ( struct nvo_block *nvo );
47
-extern void nvo_unregister ( struct nvo_block *nvo );
48
+extern void nvo_init ( struct nvo_block *nvo, struct nvs_device *nvs,
49
+		       struct nvo_fragment *fragments, struct refcnt *refcnt );
50
+extern int register_nvo ( struct nvo_block *nvo, struct settings *parent );
51
+extern void unregister_nvo ( struct nvo_block *nvo );
48 52
 
49 53
 #endif /* _GPXE_NVO_H */

+ 5
- 0
src/include/gpxe/settings.h Целия файл

@@ -151,6 +151,9 @@ extern int store_setting ( struct settings *settings, unsigned int tag,
151 151
 			   const void *data, size_t len );
152 152
 extern int fetch_setting ( struct settings *settings, unsigned int tag,
153 153
 			   void *data, size_t len );
154
+extern int copy_setting ( struct settings *dest, unsigned int dest_tag,
155
+			  struct settings *source, unsigned int source_tag );
156
+extern int copy_settings ( struct settings *dest, struct settings *source );
154 157
 extern int fetch_setting_len ( struct settings *settings, unsigned int tag );
155 158
 extern int fetch_string_setting ( struct settings *settings, unsigned int tag,
156 159
 				  char *data, size_t len );
@@ -163,6 +166,8 @@ extern int fetch_uint_setting ( struct settings *settings, unsigned int tag,
163 166
 extern long fetch_intz_setting ( struct settings *settings, unsigned int tag );
164 167
 extern unsigned long fetch_uintz_setting ( struct settings *settings,
165 168
 					   unsigned int tag );
169
+extern struct settings * find_child_settings ( struct settings *parent,
170
+					       const char *name );
166 171
 extern struct settings * find_settings ( const char *name );
167 172
 extern int store_typed_setting ( struct settings *settings,
168 173
 				 unsigned int tag, struct setting_type *type,

+ 8
- 7
src/interface/pxe/pxe_preboot.c Целия файл

@@ -28,6 +28,7 @@
28 28
 #include <stdlib.h>
29 29
 #include <gpxe/uaccess.h>
30 30
 #include <gpxe/dhcp.h>
31
+#include <gpxe/dhcppkt.h>
31 32
 #include <gpxe/device.h>
32 33
 #include <gpxe/netdevice.h>
33 34
 #include <gpxe/isapnp.h>
@@ -117,9 +118,10 @@ PXENV_EXIT_t pxenv_unload_stack ( struct s_PXENV_UNLOAD_STACK *unload_stack ) {
117 118
 PXENV_EXIT_t pxenv_get_cached_info ( struct s_PXENV_GET_CACHED_INFO
118 119
 				     *get_cached_info ) {
119 120
 	struct dhcp_packet dhcppkt;
120
-	int ( * dhcp_packet_creator ) ( struct net_device *, int,
121
-					struct dhcp_option_block *, void *,
122
-					size_t, struct dhcp_packet * );
121
+	int ( * dhcp_packet_creator ) ( struct dhcp_packet *dhcppkt,
122
+					struct net_device *netdev, int msgtype,
123
+					struct settings *settings,
124
+					void *data, size_t max_len );
123 125
 	unsigned int idx;
124 126
 	unsigned int msgtype;
125 127
 	size_t len;
@@ -149,10 +151,9 @@ PXENV_EXIT_t pxenv_get_cached_info ( struct s_PXENV_GET_CACHED_INFO
149 151
 			dhcp_packet_creator = create_dhcp_response;
150 152
 			msgtype = DHCPACK;
151 153
 		}
152
-		if ( ( rc = dhcp_packet_creator ( pxe_netdev, msgtype,
153
-						  NULL, &cached_info[idx],
154
-						  sizeof ( cached_info[idx] ),
155
-						  &dhcppkt ) ) != 0 ) {
154
+		if ( ( rc = dhcp_packet_creator ( &dhcppkt, pxe_netdev,
155
+				       msgtype, NULL, &cached_info[idx],
156
+				       sizeof ( cached_info[idx] ) ) ) != 0 ) {
156 157
 			DBG ( " failed to build packet" );
157 158
 			goto err;
158 159
 		}

+ 216
- 248
src/net/dhcpopts.c Целия файл

@@ -1,5 +1,5 @@
1 1
 /*
2
- * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
2
+ * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
3 3
  *
4 4
  * This program is free software; you can redistribute it and/or
5 5
  * modify it under the terms of the GNU General Public License as
@@ -19,14 +19,10 @@
19 19
 #include <stdint.h>
20 20
 #include <stdlib.h>
21 21
 #include <stdio.h>
22
-#include <byteswap.h>
23 22
 #include <errno.h>
24 23
 #include <string.h>
25
-#include <assert.h>
26
-#include <gpxe/list.h>
27
-#include <gpxe/in.h>
28
-#include <gpxe/uri.h>
29 24
 #include <gpxe/dhcp.h>
25
+#include <gpxe/dhcpopts.h>
30 26
 
31 27
 /** @file
32 28
  *
@@ -34,9 +30,6 @@
34 30
  *
35 31
  */
36 32
 
37
-/** List of registered DHCP option blocks */
38
-LIST_HEAD ( dhcp_option_blocks );
39
-
40 33
 /**
41 34
  * Obtain printable version of a DHCP option tag
42 35
  *
@@ -58,47 +51,28 @@ static inline char * dhcp_tag_name ( unsigned int tag ) {
58 51
 }
59 52
 
60 53
 /**
61
- * Obtain value of a numerical DHCP option
62
- *
63
- * @v option		DHCP option, or NULL
64
- * @ret value		Numerical value of the option, or 0
54
+ * Get pointer to DHCP option
65 55
  *
66
- * Parses the numerical value from a DHCP option, if present.  It is
67
- * permitted to call dhcp_num_option() with @c option set to NULL; in
68
- * this case 0 will be returned.
69
- *
70
- * The caller does not specify the size of the DHCP option data; this
71
- * is implied by the length field stored within the DHCP option
72
- * itself.
56
+ * @v options		DHCP options block
57
+ * @v offset		Offset within options block
58
+ * @ret option		DHCP option
73 59
  */
74
-unsigned long dhcp_num_option ( struct dhcp_option *option ) {
75
-	unsigned long value = 0;
76
-	uint8_t *data;
77
-
78
-	if ( option ) {
79
-		/* This is actually smaller code than using htons()
80
-		 * etc., and will also cope well with malformed
81
-		 * options (such as zero-length options).
82
-		 */
83
-		for ( data = option->data.bytes ;
84
-		      data < ( option->data.bytes + option->len ) ; data++ )
85
-			value = ( ( value << 8 ) | *data );
86
-	}
87
-	return value;
60
+static inline __attribute__ (( always_inline )) struct dhcp_option *
61
+dhcp_option ( struct dhcp_options *options, unsigned int offset ) {
62
+	return ( ( struct dhcp_option * ) ( options->data + offset ) );
88 63
 }
89 64
 
90 65
 /**
91
- * Calculate length of a normal DHCP option
66
+ * Get offset of a DHCP option
92 67
  *
68
+ * @v options		DHCP options block
93 69
  * @v option		DHCP option
94
- * @ret len		Length (including tag and length field)
95
- *
96
- * @c option may not be a @c DHCP_PAD or @c DHCP_END option.
70
+ * @ret offset		Offset within options block
97 71
  */
98
-static inline unsigned int dhcp_option_len ( struct dhcp_option *option ) {
99
-	assert ( option->tag != DHCP_PAD );
100
-	assert ( option->tag != DHCP_END );
101
-	return ( option->len + DHCP_OPTION_HEADER_LEN );
72
+static inline __attribute__ (( always_inline )) int
73
+dhcp_option_offset ( struct dhcp_options *options,
74
+		     struct dhcp_option *option ) {
75
+	return ( ( ( void * ) option ) - options->data );
102 76
 }
103 77
 
104 78
 /**
@@ -107,11 +81,11 @@ static inline unsigned int dhcp_option_len ( struct dhcp_option *option ) {
107 81
  * @v option		DHCP option
108 82
  * @ret len		Length (including tag and length field)
109 83
  */
110
-static inline unsigned int dhcp_any_option_len ( struct dhcp_option *option ) {
84
+static unsigned int dhcp_option_len ( struct dhcp_option *option ) {
111 85
 	if ( ( option->tag == DHCP_END ) || ( option->tag == DHCP_PAD ) ) {
112 86
 		return 1;
113 87
 	} else {
114
-		return dhcp_option_len ( option );
88
+		return ( option->len + DHCP_OPTION_HEADER_LEN );
115 89
 	}
116 90
 }
117 91
 
@@ -120,27 +94,27 @@ static inline unsigned int dhcp_any_option_len ( struct dhcp_option *option ) {
120 94
  *
121 95
  * @v options		DHCP options block
122 96
  * @v tag		DHCP option tag to search for
123
- * @ret encapsulator	Encapsulating DHCP option
124
- * @ret option		DHCP option, or NULL if not found
97
+ * @ret encap_offset	Offset of encapsulating DHCP option
98
+ * @ret offset		Offset of DHCP option, or negative error
125 99
  *
126 100
  * Searches for the DHCP option matching the specified tag within the
127 101
  * DHCP option block.  Encapsulated options may be searched for by
128 102
  * using DHCP_ENCAP_OPT() to construct the tag value.
129 103
  *
130 104
  * If the option is encapsulated, and @c encapsulator is non-NULL, it
131
- * will be filled in with a pointer to the encapsulating option.
105
+ * will be filled in with the offset of the encapsulating option.
132 106
  *
133 107
  * This routine is designed to be paranoid.  It does not assume that
134 108
  * the option data is well-formatted, and so must guard against flaws
135 109
  * such as options missing a @c DHCP_END terminator, or options whose
136 110
  * length would take them beyond the end of the data block.
137 111
  */
138
-static struct dhcp_option *
139
-find_dhcp_option_with_encap ( struct dhcp_option_block *options,
140
-			      unsigned int tag,
141
-			      struct dhcp_option **encapsulator ) {
112
+static int find_dhcp_option_with_encap ( struct dhcp_options *options,
113
+					 unsigned int tag,
114
+					 int *encap_offset ) {
142 115
 	unsigned int original_tag __attribute__ (( unused )) = tag;
143
-	struct dhcp_option *option = options->data;
116
+	struct dhcp_option *option;
117
+	int offset = 0;
144 118
 	ssize_t remaining = options->len;
145 119
 	unsigned int option_len;
146 120
 
@@ -149,16 +123,17 @@ find_dhcp_option_with_encap ( struct dhcp_option_block *options,
149 123
 		 * if the length is malformed (i.e. takes us beyond
150 124
 		 * the end of the data block).
151 125
 		 */
152
-		option_len = dhcp_any_option_len ( option );
126
+		option = dhcp_option ( options, offset );
127
+		option_len = dhcp_option_len ( option );
153 128
 		remaining -= option_len;
154 129
 		if ( remaining < 0 )
155 130
 			break;
156 131
 		/* Check for matching tag */
157 132
 		if ( option->tag == tag ) {
158
-			DBG ( "Found DHCP option %s (length %d) in block %p\n",
159
-			      dhcp_tag_name ( original_tag ), option->len,
160
-			      options );
161
-			return option;
133
+			DBGC ( options, "DHCPOPT %p found %s (length %d)\n",
134
+			       options, dhcp_tag_name ( original_tag ),
135
+			       option_len );
136
+			return offset;
162 137
 		}
163 138
 		/* Check for explicit end marker */
164 139
 		if ( option->tag == DHCP_END )
@@ -166,168 +141,74 @@ find_dhcp_option_with_encap ( struct dhcp_option_block *options,
166 141
 		/* Check for start of matching encapsulation block */
167 142
 		if ( DHCP_IS_ENCAP_OPT ( tag ) &&
168 143
 		     ( option->tag == DHCP_ENCAPSULATOR ( tag ) ) ) {
169
-			if ( encapsulator )
170
-				*encapsulator = option;
144
+			if ( encap_offset )
145
+				*encap_offset = offset;
171 146
 			/* Continue search within encapsulated option block */
172 147
 			tag = DHCP_ENCAPSULATED ( tag );
173
-			remaining = option->len;
174
-			option = ( void * ) &option->data;
148
+			remaining = option_len;
149
+			offset += DHCP_OPTION_HEADER_LEN;
175 150
 			continue;
176 151
 		}
177
-		option = ( ( ( void * ) option ) + option_len );
178
-	}
179
-	return NULL;
180
-}
181
-
182
-/**
183
- * Find DHCP option within DHCP options block
184
- *
185
- * @v options		DHCP options block, or NULL
186
- * @v tag		DHCP option tag to search for
187
- * @ret option		DHCP option, or NULL if not found
188
- *
189
- * Searches for the DHCP option matching the specified tag within the
190
- * DHCP option block.  Encapsulated options may be searched for by
191
- * using DHCP_ENCAP_OPT() to construct the tag value.
192
- *
193
- * If @c options is NULL, all registered option blocks will be
194
- * searched.  Where multiple option blocks contain the same DHCP
195
- * option, the option from the highest-priority block will be
196
- * returned.  (Priority of an options block is determined by the value
197
- * of the @c DHCP_EB_PRIORITY option within the block, if present; the
198
- * default priority is zero).
199
- */
200
-struct dhcp_option * find_dhcp_option ( struct dhcp_option_block *options,
201
-					unsigned int tag ) {
202
-	struct dhcp_option *option;
203
-
204
-	if ( options ) {
205
-		return find_dhcp_option_with_encap ( options, tag, NULL );
206
-	} else {
207
-		list_for_each_entry ( options, &dhcp_option_blocks, list ) {
208
-			if ( ( option = find_dhcp_option ( options, tag ) ) )
209
-				return option;
210
-		}
211
-		return NULL;
212
-	}
213
-}
214
-
215
-/**
216
- * Register DHCP option block
217
- *
218
- * @v options		DHCP option block
219
- *
220
- * Register a block of DHCP options.
221
- */
222
-void register_dhcp_options ( struct dhcp_option_block *options ) {
223
-	struct dhcp_option_block *existing;
224
-
225
-	/* Determine priority of new block */
226
-	options->priority = find_dhcp_num_option ( options, DHCP_EB_PRIORITY );
227
-	DBG ( "Registering DHCP options block %p with priority %d\n",
228
-	      options, options->priority );
229
-
230
-	/* Insert after any existing blocks which have a higher priority */
231
-	list_for_each_entry ( existing, &dhcp_option_blocks, list ) {
232
-		if ( options->priority > existing->priority )
233
-			break;
234
-	}
235
-	dhcpopt_get ( options );
236
-	list_add_tail ( &options->list, &existing->list );
237
-
238
-}
239
-
240
-/**
241
- * Unregister DHCP option block
242
- *
243
- * @v options		DHCP option block
244
- */
245
-void unregister_dhcp_options ( struct dhcp_option_block *options ) {
246
-	list_del ( &options->list );
247
-	dhcpopt_put ( options );
248
-}
249
-
250
-/**
251
- * Initialise empty block of DHCP options
252
- *
253
- * @v options		Uninitialised DHCP option block
254
- * @v data		Memory for DHCP option data
255
- * @v max_len		Length of memory for DHCP option data
256
- *
257
- * Populates the DHCP option data with a single @c DHCP_END option and
258
- * fills in the fields of the @c dhcp_option_block structure.
259
- */
260
-void init_dhcp_options ( struct dhcp_option_block *options,
261
-			 void *data, size_t max_len ) {
262
-	struct dhcp_option *option;
263
-
264
-	options->data = data;
265
-	options->max_len = max_len;
266
-	option = options->data;
267
-	option->tag = DHCP_END;
268
-	options->len = 1;
269
-
270
-	DBG ( "DHCP options block %p initialised (data %p max_len %#zx)\n",
271
-	      options, options->data, options->max_len );
272
-}
273
-
274
-/**
275
- * Allocate space for a block of DHCP options
276
- *
277
- * @v max_len		Maximum length of option block
278
- * @ret options		DHCP option block, or NULL
279
- *
280
- * Creates a new DHCP option block and populates it with an empty
281
- * options list.  This call does not register the options block.
282
- */
283
-struct dhcp_option_block * alloc_dhcp_options ( size_t max_len ) {
284
-	struct dhcp_option_block *options;
285
-
286
-	options = malloc ( sizeof ( *options ) + max_len );
287
-	if ( options ) {
288
-		init_dhcp_options ( options, 
289
-				    ( (void *) options + sizeof ( *options ) ),
290
-				    max_len );
152
+		offset += option_len;
291 153
 	}
292
-	return options;
154
+	return -ENOENT;
293 155
 }
294 156
 
295 157
 /**
296 158
  * Resize a DHCP option
297 159
  *
298 160
  * @v options		DHCP option block
299
- * @v option		DHCP option to resize
300
- * @v encapsulator	Encapsulating option (or NULL)
161
+ * @v offset		Offset of option to resize
162
+ * @v encap_offset	Offset of encapsulating offset (or -ve for none)
301 163
  * @v old_len		Old length (including header)
302 164
  * @v new_len		New length (including header)
165
+ * @v can_realloc	Can reallocate options data if necessary
303 166
  * @ret rc		Return status code
304 167
  */
305
-static int resize_dhcp_option ( struct dhcp_option_block *options,
306
-				struct dhcp_option *option,
307
-				struct dhcp_option *encapsulator,
308
-				size_t old_len, size_t new_len ) {
309
-	void *source = ( ( ( void * ) option ) + old_len );
310
-	void *dest = ( ( ( void * ) option ) + new_len );
311
-	void *end = ( options->data + options->max_len );
168
+static int resize_dhcp_option ( struct dhcp_options *options,
169
+				int offset, int encap_offset,
170
+				size_t old_len, size_t new_len,
171
+				int can_realloc ) {
172
+	struct dhcp_option *encapsulator;
173
+	struct dhcp_option *option;
312 174
 	ssize_t delta = ( new_len - old_len );
313 175
 	size_t new_options_len;
314 176
 	size_t new_encapsulator_len;
177
+	void *new_data;
178
+	void *source;
179
+	void *dest;
180
+	void *end;
315 181
 
316 182
 	/* Check for sufficient space, and update length fields */
317 183
 	if ( new_len > DHCP_MAX_LEN )
318
-		return -ENOMEM;
184
+		return -ENOSPC;
319 185
 	new_options_len = ( options->len + delta );
320
-	if ( new_options_len > options->max_len )
321
-		return -ENOMEM;
322
-	if ( encapsulator ) {
186
+	if ( new_options_len > options->max_len ) {
187
+		/* Reallocate options block if allowed to do so. */
188
+		if ( can_realloc ) {
189
+			new_data = realloc ( options->data, new_options_len );
190
+			if ( ! new_data )
191
+				return -ENOMEM;
192
+			options->data = new_data;
193
+			options->max_len = new_options_len;
194
+		} else {
195
+			return -ENOMEM;
196
+		}
197
+	}
198
+	if ( encap_offset >= 0 ) {
199
+		encapsulator = dhcp_option ( options, encap_offset );
323 200
 		new_encapsulator_len = ( encapsulator->len + delta );
324 201
 		if ( new_encapsulator_len > DHCP_MAX_LEN )
325
-			return -ENOMEM;
202
+			return -ENOSPC;
326 203
 		encapsulator->len = new_encapsulator_len;
327 204
 	}
328 205
 	options->len = new_options_len;
329 206
 
330 207
 	/* Move remainder of option data */
208
+	option = dhcp_option ( options, offset );
209
+	source = ( ( ( void * ) option ) + old_len );
210
+	dest = ( ( ( void * ) option ) + new_len );
211
+	end = ( options->data + options->max_len );
331 212
 	memmove ( dest, source, ( end - dest ) );
332 213
 
333 214
 	return 0;
@@ -340,7 +221,8 @@ static int resize_dhcp_option ( struct dhcp_option_block *options,
340 221
  * @v tag		DHCP option tag
341 222
  * @v data		New value for DHCP option
342 223
  * @v len		Length of value, in bytes
343
- * @ret option		DHCP option, or NULL
224
+ * @v can_realloc	Can reallocate options data if necessary
225
+ * @ret offset		Offset of DHCP option, or negative error
344 226
  *
345 227
  * Sets the value of a DHCP option within the options block.  The
346 228
  * option may or may not already exist.  Encapsulators will be created
@@ -350,97 +232,183 @@ static int resize_dhcp_option ( struct dhcp_option_block *options,
350 232
  * If it does fail, and the option existed previously, the option will
351 233
  * be left with its original value.
352 234
  */
353
-struct dhcp_option * set_dhcp_option ( struct dhcp_option_block *options,
354
-				       unsigned int tag,
355
-				       const void *data, size_t len ) {
235
+static int set_dhcp_option ( struct dhcp_options *options, unsigned int tag,
236
+			     const void *data, size_t len,
237
+			     int can_realloc ) {
356 238
 	static const uint8_t empty_encapsulator[] = { DHCP_END };
239
+	int offset;
240
+	int encap_offset = -1;
241
+	int creation_offset = 0;
357 242
 	struct dhcp_option *option;
358
-	void *insertion_point;
359
-	struct dhcp_option *encapsulator = NULL;
360 243
 	unsigned int encap_tag = DHCP_ENCAPSULATOR ( tag );
361 244
 	size_t old_len = 0;
362 245
 	size_t new_len = ( len ? ( len + DHCP_OPTION_HEADER_LEN ) : 0 );
363
-
364
-	/* Return NULL if no options block specified */
365
-	if ( ! options )
366
-		return NULL;
246
+	int rc;
367 247
 
368 248
 	/* Find old instance of this option, if any */
369
-	option = find_dhcp_option_with_encap ( options, tag, &encapsulator );
370
-	if ( option ) {
371
-		old_len = dhcp_option_len ( option );
372
-		DBG ( "Resizing DHCP option %s from length %d to %zd in block "
373
-		      "%p\n", dhcp_tag_name (tag), option->len, len, options );
249
+	offset = find_dhcp_option_with_encap ( options, tag, &encap_offset );
250
+	if ( offset >= 0 ) {
251
+		old_len = dhcp_option_len ( dhcp_option ( options, offset ) );
252
+		DBGC ( options, "DHCPOPT %p resizing %s from %zd to %zd\n",
253
+		       options, dhcp_tag_name ( tag ), old_len, new_len );
374 254
 	} else {
375
-		old_len = 0;
376
-		DBG ( "Creating DHCP option %s (length %zd) in block %p\n",
377
-		      dhcp_tag_name ( tag ), len, options );
255
+		DBGC ( options, "DHCPOPT %p creating %s (length %zd)\n",
256
+		       options, dhcp_tag_name ( tag ), len );
378 257
 	}
379
-	
258
+
380 259
 	/* Ensure that encapsulator exists, if required */
381
-	insertion_point = options->data;
382
-	if ( DHCP_IS_ENCAP_OPT ( tag ) ) {
383
-		if ( ! encapsulator )
384
-			encapsulator = set_dhcp_option ( options, encap_tag,
385
-							 empty_encapsulator,
386
-						sizeof ( empty_encapsulator) );
387
-		if ( ! encapsulator )
388
-			return NULL;
389
-		insertion_point = &encapsulator->data;
260
+	if ( encap_tag ) {
261
+		if ( encap_offset < 0 )
262
+			encap_offset = set_dhcp_option ( options, encap_tag,
263
+							 empty_encapsulator, 1,
264
+							 can_realloc );
265
+		if ( encap_offset < 0 )
266
+			return encap_offset;
267
+		creation_offset = ( encap_offset + DHCP_OPTION_HEADER_LEN );
390 268
 	}
391 269
 
392 270
 	/* Create new option if necessary */
393
-	if ( ! option )
394
-		option = insertion_point;
395
-	
271
+	if ( offset < 0 )
272
+		offset = creation_offset;
273
+
396 274
 	/* Resize option to fit new data */
397
-	if ( resize_dhcp_option ( options, option, encapsulator,
398
-				  old_len, new_len ) != 0 )
399
-		return NULL;
275
+	if ( ( rc = resize_dhcp_option ( options, offset, encap_offset,
276
+					 old_len, new_len,
277
+					 can_realloc ) ) != 0 )
278
+		return rc;
400 279
 
401 280
 	/* Copy new data into option, if applicable */
402 281
 	if ( len ) {
282
+		option = dhcp_option ( options, offset );
403 283
 		option->tag = tag;
404 284
 		option->len = len;
405 285
 		memcpy ( &option->data, data, len );
406 286
 	}
407 287
 
408 288
 	/* Delete encapsulator if there's nothing else left in it */
409
-	if ( encapsulator && ( encapsulator->len <= 1 ) )
410
-		set_dhcp_option ( options, encap_tag, NULL, 0 );
289
+	if ( encap_offset >= 0 ) {
290
+		option = dhcp_option ( options, encap_offset );
291
+		if ( option->len <= 1 )
292
+			set_dhcp_option ( options, encap_tag, NULL, 0, 0 );
293
+	}
411 294
 
412
-	return option;
295
+	return offset;
413 296
 }
414 297
 
415 298
 /**
416
- * Find DHCP numerical option, and return its value
299
+ * Store value of DHCP option setting
417 300
  *
418
- * @v options		DHCP options block
419
- * @v tag		DHCP option tag to search for
420
- * @ret value		Numerical value of the option, or 0 if not found
301
+ * @v options		DHCP option block
302
+ * @v tag		Setting tag number
303
+ * @v data		Setting data, or NULL to clear setting
304
+ * @v len		Length of setting data
305
+ * @ret rc		Return status code
306
+ */
307
+int dhcpopt_store ( struct dhcp_options *options, unsigned int tag,
308
+		    const void *data, size_t len ) {
309
+	int offset;
310
+
311
+	offset = set_dhcp_option ( options, tag, data, len, 0 );
312
+	if ( offset < 0 )
313
+		return offset;
314
+	return 0;
315
+}
316
+
317
+/**
318
+ * Store value of DHCP option setting, extending options block if necessary
421 319
  *
422
- * This function exists merely as a notational shorthand for a call to
423
- * find_dhcp_option() followed by a call to dhcp_num_option().  It is
424
- * not possible to distinguish between the cases "option not found"
425
- * and "option has a value of zero" using this function; if this
426
- * matters to you then issue the two constituent calls directly and
427
- * check that find_dhcp_option() returns a non-NULL value.
320
+ * @v options		DHCP option block
321
+ * @v tag		Setting tag number
322
+ * @v data		Setting data, or NULL to clear setting
323
+ * @v len		Length of setting data
324
+ * @ret rc		Return status code
428 325
  */
429
-unsigned long find_dhcp_num_option ( struct dhcp_option_block *options,
430
-				     unsigned int tag ) {
431
-	return dhcp_num_option ( find_dhcp_option ( options, tag ) );
326
+int dhcpopt_extensible_store ( struct dhcp_options *options, unsigned int tag,
327
+			       const void *data, size_t len ) {
328
+	int offset;
329
+
330
+	offset = set_dhcp_option ( options, tag, data, len, 1 );
331
+	if ( offset < 0 )
332
+		return offset;
333
+	return 0;
432 334
 }
433 335
 
434 336
 /**
435
- * Delete DHCP option
337
+ * Fetch value of DHCP option setting
436 338
  *
437
- * @v options		DHCP options block
438
- * @v tag		DHCP option tag
339
+ * @v options		DHCP option block
340
+ * @v tag		Setting tag number
341
+ * @v data		Buffer to fill with setting data
342
+ * @v len		Length of buffer
343
+ * @ret len		Length of setting data, or negative error
344
+ */
345
+int dhcpopt_fetch ( struct dhcp_options *options, unsigned int tag,
346
+		    void *data, size_t len ) {
347
+	int offset;
348
+	struct dhcp_option *option;
349
+	size_t option_len;
350
+
351
+	offset = find_dhcp_option_with_encap ( options, tag, NULL );
352
+	if ( offset < 0 )
353
+		return offset;
354
+
355
+	option = dhcp_option ( options, offset );
356
+	option_len = dhcp_option_len ( option );
357
+	if ( len > option_len )
358
+		len = option_len;
359
+	memcpy ( data, option->data, len );
360
+
361
+	return option_len;
362
+}
363
+
364
+/**
365
+ * Recalculate length of DHCP options block
366
+ *
367
+ * @v options		Uninitialised DHCP option block
439 368
  *
440
- * This function exists merely as a notational shorthand for a call to
441
- * set_dhcp_option() with @c len set to zero.
369
+ * The "used length" field will be updated based on scanning through
370
+ * the block to find the end of the options.
442 371
  */
443
-void delete_dhcp_option ( struct dhcp_option_block *options,
444
-			  unsigned int tag ) {
445
-	set_dhcp_option ( options, tag, NULL, 0 );
372
+static void dhcpopt_update_len ( struct dhcp_options *options ) {
373
+	struct dhcp_option *option;
374
+	int offset = 0;
375
+	ssize_t remaining = options->max_len;
376
+	unsigned int option_len;
377
+
378
+	/* Find last non-pad option */
379
+	options->len = 0;
380
+	while ( remaining ) {
381
+		option = dhcp_option ( options, offset );
382
+		option_len = dhcp_option_len ( option );
383
+		remaining -= option_len;
384
+		if ( remaining < 0 )
385
+			break;
386
+		offset += option_len;
387
+		if ( option->tag != DHCP_PAD )
388
+			options->len = offset;
389
+	}
390
+}
391
+
392
+/**
393
+ * Initialise prepopulated block of DHCP options
394
+ *
395
+ * @v options		Uninitialised DHCP option block
396
+ * @v data		Memory for DHCP option data
397
+ * @v max_len		Length of memory for DHCP option data
398
+ *
399
+ * The memory content must already be filled with valid DHCP options.
400
+ * A zeroed block counts as a block of valid DHCP options.
401
+ */
402
+void dhcpopt_init ( struct dhcp_options *options, void *data,
403
+		    size_t max_len ) {
404
+
405
+	/* Fill in fields */
406
+	options->data = data;
407
+	options->max_len = max_len;
408
+
409
+	/* Update length */
410
+	dhcpopt_update_len ( options );
411
+
412
+	DBGC ( options, "DHCPOPT %p created (data %p len %#zx max_len %#zx)\n",
413
+	       options, options->data, options->len, options->max_len );
446 414
 }

+ 186
- 0
src/net/dhcppkt.c Целия файл

@@ -0,0 +1,186 @@
1
+/*
2
+ * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
3
+ *
4
+ * This program is free software; you can redistribute it and/or
5
+ * modify it under the terms of the GNU General Public License as
6
+ * published by the Free Software Foundation; either version 2 of the
7
+ * License, or any later version.
8
+ *
9
+ * This program is distributed in the hope that it will be useful, but
10
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
+ * General Public License for more details.
13
+ *
14
+ * You should have received a copy of the GNU General Public License
15
+ * along with this program; if not, write to the Free Software
16
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
+ */
18
+
19
+#include <stdint.h>
20
+#include <stdlib.h>
21
+#include <stdio.h>
22
+#include <errno.h>
23
+#include <string.h>
24
+#include <gpxe/netdevice.h>
25
+#include <gpxe/dhcp.h>
26
+#include <gpxe/dhcpopts.h>
27
+#include <gpxe/dhcppkt.h>
28
+
29
+/** @file
30
+ *
31
+ * DHCP packets
32
+ *
33
+ */
34
+
35
+/** A dedicated field within a DHCP packet */
36
+struct dhcp_packet_field {
37
+	/** Settings tag number */
38
+	unsigned int tag;
39
+	/** Offset within DHCP packet */
40
+	uint16_t offset;
41
+	/** Length of field */
42
+	uint16_t len;
43
+};
44
+
45
+/** Declare a dedicated field within a DHCP packet
46
+ *
47
+ * @v _tag		Settings tag number
48
+ * @v _field		Field name
49
+ */
50
+#define DHCP_PACKET_FIELD( _tag, _field ) {				\
51
+		.tag = (_tag),						\
52
+		.offset = offsetof ( struct dhcphdr, _field ),		\
53
+		.len = sizeof ( ( ( struct dhcphdr * ) 0 )->_field ),	\
54
+	}
55
+
56
+/** Dedicated fields within a DHCP packet */
57
+static struct dhcp_packet_field dhcp_packet_fields[] = {
58
+	DHCP_PACKET_FIELD ( DHCP_EB_YIADDR, yiaddr ),
59
+	DHCP_PACKET_FIELD ( DHCP_EB_SIADDR, siaddr ),
60
+	DHCP_PACKET_FIELD ( DHCP_TFTP_SERVER_NAME, sname ),
61
+	DHCP_PACKET_FIELD ( DHCP_BOOTFILE_NAME, file ),
62
+};
63
+
64
+/**
65
+ * Get address of a DHCP packet field
66
+ *
67
+ * @v dhcphdr		DHCP packet header
68
+ * @v field		DHCP packet field
69
+ * @ret data		Packet field data
70
+ */
71
+static inline void * dhcp_packet_field ( struct dhcphdr *dhcphdr,
72
+					 struct dhcp_packet_field *field ) {
73
+	return ( ( ( void * ) dhcphdr ) + field->offset );
74
+}
75
+
76
+/**
77
+ * Find DHCP packet field corresponding to settings tag number
78
+ *
79
+ * @v tag		Settings tag number
80
+ * @ret field		DHCP packet field, or NULL
81
+ */
82
+static struct dhcp_packet_field *
83
+find_dhcp_packet_field ( unsigned int tag ) {
84
+	struct dhcp_packet_field *field;
85
+	unsigned int i;
86
+
87
+	for ( i = 0 ; i < ( sizeof ( dhcp_packet_fields ) /
88
+			    sizeof ( dhcp_packet_fields[0] ) ) ; i++ ) {
89
+		field = &dhcp_packet_fields[i];
90
+		if ( field->tag == tag )
91
+			return field;
92
+	}
93
+	return NULL;
94
+}
95
+				    
96
+/**
97
+ * Store value of DHCP packet setting
98
+ *
99
+ * @v settings		Settings block
100
+ * @v tag		Setting tag number
101
+ * @v data		Setting data, or NULL to clear setting
102
+ * @v len		Length of setting data
103
+ * @ret rc		Return status code
104
+ */
105
+static int dhcppkt_store ( struct settings *settings, unsigned int tag,
106
+			   const void *data, size_t len ) {
107
+	struct dhcp_packet *dhcppkt =
108
+		container_of ( settings, struct dhcp_packet, settings );
109
+	struct dhcp_packet_field *field;
110
+	int rc;
111
+
112
+	/* If this is a special field, fill it in */
113
+	if ( ( field = find_dhcp_packet_field ( tag ) ) != NULL ) {
114
+		if ( len > field->len )
115
+			return -ENOSPC;
116
+		memcpy ( dhcp_packet_field ( dhcppkt->dhcphdr, field ),
117
+			 data, len );
118
+		return 0;
119
+	}
120
+
121
+	/* Otherwise, use the generic options block */
122
+	rc = dhcpopt_store ( &dhcppkt->options, tag, data, len );
123
+
124
+	/* Update our used-length field */
125
+	dhcppkt->len = ( offsetof ( struct dhcphdr, options ) +
126
+			 dhcppkt->options.len );
127
+
128
+	return rc;
129
+}
130
+
131
+/**
132
+ * Fetch value of DHCP packet setting
133
+ *
134
+ * @v settings		Settings block
135
+ * @v tag		Setting tag number
136
+ * @v data		Buffer to fill with setting data
137
+ * @v len		Length of buffer
138
+ * @ret len		Length of setting data, or negative error
139
+ */
140
+static int dhcppkt_fetch ( struct settings *settings, unsigned int tag,
141
+			   void *data, size_t len ) {
142
+	struct dhcp_packet *dhcppkt =
143
+		container_of ( settings, struct dhcp_packet, settings );
144
+	struct dhcp_packet_field *field;
145
+	
146
+	/* If this is a special field, return it */
147
+	if ( ( field = find_dhcp_packet_field ( tag ) ) != NULL ) {
148
+		if ( len > field->len )
149
+			len = field->len;
150
+		memcpy ( data,
151
+			 dhcp_packet_field ( dhcppkt->dhcphdr, field ), len );
152
+		return field->len;
153
+	}
154
+
155
+	/* Otherwise, use the generic options block */
156
+	return dhcpopt_fetch ( &dhcppkt->options, tag, data, len );
157
+}
158
+
159
+/** DHCP settings operations */
160
+static struct settings_operations dhcppkt_settings_operations = {
161
+	.store = dhcppkt_store,
162
+	.fetch = dhcppkt_fetch,
163
+};
164
+
165
+/**
166
+ * Initialise prepopulated DHCP packet
167
+ *
168
+ * @v dhcppkt		Uninitialised DHCP packet
169
+ * @v refcnt		Reference counter of containing object, or NULL
170
+ * @v data		Memory for DHCP packet data
171
+ * @v max_len		Length of memory for DHCP packet data
172
+ *
173
+ * The memory content must already be filled with valid DHCP options.
174
+ * A zeroed block counts as a block of valid DHCP options.
175
+ */
176
+void dhcppkt_init ( struct dhcp_packet *dhcppkt, struct refcnt *refcnt,
177
+		    void *data, size_t len ) {
178
+	dhcppkt->dhcphdr = data;
179
+	dhcppkt->max_len = len;
180
+	dhcpopt_init ( &dhcppkt->options, &dhcppkt->dhcphdr->options,
181
+		       ( len - offsetof ( struct dhcphdr, options ) ) );
182
+	dhcppkt->len = ( offsetof ( struct dhcphdr, options ) +
183
+			 dhcppkt->options.len );
184
+	settings_init ( &dhcppkt->settings, &dhcppkt_settings_operations,
185
+			refcnt, "dhcp" );
186
+}

+ 197
- 437
src/net/udp/dhcp.c Целия файл

@@ -34,6 +34,10 @@
34 34
 #include <gpxe/uuid.h>
35 35
 #include <gpxe/dhcp.h>
36 36
 #include <gpxe/timer.h>
37
+#include <gpxe/settings.h>
38
+#include <gpxe/dhcp.h>
39
+#include <gpxe/dhcpopts.h>
40
+#include <gpxe/dhcppkt.h>
37 41
 
38 42
 /** @file
39 43
  *
@@ -41,7 +45,8 @@
41 45
  *
42 46
  */
43 47
 
44
-/** DHCP operation types
48
+/**
49
+ * DHCP operation types
45 50
  *
46 51
  * This table maps from DHCP message types (i.e. values of the @c
47 52
  * DHCP_MESSAGE_TYPE option) to values of the "op" field within a DHCP
@@ -76,6 +81,13 @@ static uint8_t dhcp_request_options_data[] = {
76 81
 	DHCP_END
77 82
 };
78 83
 
84
+/** Options common to all DHCP requests */
85
+static struct dhcp_options dhcp_request_options = {
86
+	.data = dhcp_request_options_data,
87
+	.max_len = sizeof ( dhcp_request_options_data ),
88
+	.len = sizeof ( dhcp_request_options_data ),
89
+};
90
+
79 91
 /** DHCP feature codes */
80 92
 static uint8_t dhcp_features[0] __table_start ( uint8_t, dhcp_features );
81 93
 static uint8_t dhcp_features_end[0] __table_end ( uint8_t, dhcp_features );
@@ -118,197 +130,33 @@ static uint32_t dhcp_xid ( struct net_device *netdev ) {
118 130
 	return xid;
119 131
 }
120 132
 
121
-/** Options common to all DHCP requests */
122
-static struct dhcp_option_block dhcp_request_options = {
123
-	.data = dhcp_request_options_data,
124
-	.max_len = sizeof ( dhcp_request_options_data ),
125
-	.len = sizeof ( dhcp_request_options_data ),
126
-};
127
-
128
-/**
129
- * Set option within DHCP packet
130
- *
131
- * @v dhcppkt		DHCP packet
132
- * @v tag		DHCP option tag
133
- * @v data		New value for DHCP option
134
- * @v len		Length of value, in bytes
135
- * @ret rc		Return status code
136
- *
137
- * Sets the option within the first available options block within the
138
- * DHCP packet.  Option blocks are tried in the order specified by @c
139
- * dhcp_option_block_fill_order.
140
- *
141
- * The magic options @c DHCP_EB_YIADDR and @c DHCP_EB_SIADDR are
142
- * intercepted and inserted into the appropriate fixed fields within
143
- * the DHCP packet.  The option @c DHCP_OPTION_OVERLOAD is silently
144
- * ignored, since our DHCP packet assembly method relies on always
145
- * having option overloading in use.
146
- */
147
-static int set_dhcp_packet_option ( struct dhcp_packet *dhcppkt,
148
-				    unsigned int tag, const void *data,
149
-				    size_t len ) {
150
-	struct dhcphdr *dhcphdr = dhcppkt->dhcphdr;
151
-	struct dhcp_option_block *options = &dhcppkt->options;
152
-	struct dhcp_option *option = NULL;
153
-
154
-	/* Special-case the magic options */
155
-	switch ( tag ) {
156
-	case DHCP_OPTION_OVERLOAD:
157
-		/* Hard-coded in packets we create; always ignore */
158
-		return 0;
159
-	case DHCP_EB_YIADDR:
160
-		memcpy ( &dhcphdr->yiaddr, data, sizeof ( dhcphdr->yiaddr ) );
161
-		return 0;
162
-	case DHCP_EB_SIADDR:
163
-		memcpy ( &dhcphdr->siaddr, data, sizeof ( dhcphdr->siaddr ) );
164
-		return 0;
165
-	case DHCP_TFTP_SERVER_NAME:
166
-		memset ( dhcphdr->sname, 0, sizeof ( dhcphdr->sname ) );
167
-		if ( len > sizeof ( dhcphdr->sname ) )
168
-			len = sizeof ( dhcphdr->sname );
169
-		memcpy ( dhcphdr->sname, data, len );
170
-		return 0;
171
-	case DHCP_BOOTFILE_NAME:
172
-		memset ( dhcphdr->file, 0, sizeof ( dhcphdr->file ) );
173
-		if ( len > sizeof ( dhcphdr->file ) )
174
-			len = sizeof ( dhcphdr->file );
175
-		memcpy ( dhcphdr->file, data, len );
176
-		return 0;
177
-	default:
178
-		/* Continue processing as normal */
179
-		break;
180
-	}
181
-		
182
-	/* Set option */
183
-	option = set_dhcp_option ( options, tag, data, len );
184
-
185
-	/* Update DHCP packet length */
186
-	dhcppkt->len = ( offsetof ( typeof ( *dhcppkt->dhcphdr ), options )
187
-			 + dhcppkt->options.len );
188
-
189
-	return ( option ? 0 : -ENOSPC );
190
-}
191
-
192
-/**
193
- * Copy option into DHCP packet
194
- *
195
- * @v dhcppkt		DHCP packet
196
- * @v options		DHCP option block, or NULL
197
- * @v tag		DHCP option tag to search for
198
- * @v new_tag		DHCP option tag to use for copied option
199
- * @ret rc		Return status code
200
- *
201
- * Copies a single option, if present, from the DHCP options block
202
- * into a DHCP packet.  The tag for the option may be changed if
203
- * desired; this is required by other parts of the DHCP code.
204
- *
205
- * @c options may specify a single options block, or be left as NULL
206
- * in order to search for the option within all registered options
207
- * blocks.
208
- */
209
-static int copy_dhcp_packet_option ( struct dhcp_packet *dhcppkt,
210
-				     struct dhcp_option_block *options,
211
-				     unsigned int tag, unsigned int new_tag ) {
212
-	struct dhcp_option *option;
213
-	int rc;
214
-
215
-	option = find_dhcp_option ( options, tag );
216
-	if ( option ) {
217
-		if ( ( rc = set_dhcp_packet_option ( dhcppkt, new_tag,
218
-						     &option->data,
219
-						     option->len ) ) != 0 )
220
-			return rc;
221
-	}
222
-	return 0;
223
-}
224
-
225
-/**
226
- * Copy options into DHCP packet
227
- *
228
- * @v dhcppkt		DHCP packet
229
- * @v options		DHCP option block, or NULL
230
- * @v encapsulator	Encapsulating option, or zero
231
- * @ret rc		Return status code
232
- * 
233
- * Copies options with the specified encapsulator from DHCP options
234
- * blocks into a DHCP packet.  Most options are copied verbatim.
235
- * Recognised encapsulated options fields are handled as such.
236
- *
237
- * @c options may specify a single options block, or be left as NULL
238
- * in order to copy options from all registered options blocks.
239
- */
240
-static int copy_dhcp_packet_encap_options ( struct dhcp_packet *dhcppkt,
241
-					    struct dhcp_option_block *options,
242
-					    unsigned int encapsulator ) {
243
-	unsigned int subtag;
244
-	unsigned int tag;
245
-	int rc;
246
-
247
-	for ( subtag = DHCP_MIN_OPTION; subtag <= DHCP_MAX_OPTION; subtag++ ) {
248
-		tag = DHCP_ENCAP_OPT ( encapsulator, subtag );
249
-		switch ( tag ) {
250
-		case DHCP_EB_ENCAP:
251
-		case DHCP_VENDOR_ENCAP:
252
-			/* Process encapsulated options field */
253
-			if ( ( rc = copy_dhcp_packet_encap_options ( dhcppkt,
254
-								     options,
255
-								     tag)) !=0)
256
-				return rc;
257
-			break;
258
-		default:
259
-			/* Copy option to reassembled packet */
260
-			if ( ( rc = copy_dhcp_packet_option ( dhcppkt, options,
261
-							      tag, tag ) ) !=0)
262
-				return rc;
263
-			break;
264
-		};
265
-	}
266
-
267
-	return 0;
268
-}
269
-
270
-/**
271
- * Copy options into DHCP packet
272
- *
273
- * @v dhcppkt		DHCP packet
274
- * @v options		DHCP option block, or NULL
275
- * @ret rc		Return status code
276
- * 
277
- * Copies options from DHCP options blocks into a DHCP packet.  Most
278
- * options are copied verbatim.  Recognised encapsulated options
279
- * fields are handled as such.
280
- *
281
- * @c options may specify a single options block, or be left as NULL
282
- * in order to copy options from all registered options blocks.
283
- */
284
-static int copy_dhcp_packet_options ( struct dhcp_packet *dhcppkt,
285
-				      struct dhcp_option_block *options ) {
286
-	return copy_dhcp_packet_encap_options ( dhcppkt, options, 0 );
287
-}
288
-
289 133
 /**
290 134
  * Create a DHCP packet
291 135
  *
136
+ * @v dhcppkt		DHCP packet structure to fill in
292 137
  * @v netdev		Network device
293 138
  * @v msgtype		DHCP message type
139
+ * @v options		Initial options to include (or NULL)
294 140
  * @v data		Buffer for DHCP packet
295 141
  * @v max_len		Size of DHCP packet buffer
296
- * @v dhcppkt		DHCP packet structure to fill in
297 142
  * @ret rc		Return status code
298 143
  *
299 144
  * Creates a DHCP packet in the specified buffer, and fills out a @c
300 145
  * dhcp_packet structure that can be passed to
301 146
  * set_dhcp_packet_option() or copy_dhcp_packet_options().
302 147
  */
303
-static int create_dhcp_packet ( struct net_device *netdev, uint8_t msgtype,
304
-				void *data, size_t max_len,
305
-				struct dhcp_packet *dhcppkt ) {
148
+static int create_dhcp_packet ( struct dhcp_packet *dhcppkt,
149
+				struct net_device *netdev, uint8_t msgtype,
150
+				struct dhcp_options *options, 
151
+				void *data, size_t max_len ) {
306 152
 	struct dhcphdr *dhcphdr = data;
153
+	size_t options_len;
307 154
 	unsigned int hlen;
308 155
 	int rc;
309 156
 
310 157
 	/* Sanity check */
311
-	if ( max_len < sizeof ( *dhcphdr ) )
158
+	options_len = ( options ? options->len : 0 );
159
+	if ( max_len < ( sizeof ( *dhcphdr ) + options_len ) )
312 160
 		return -ENOSPC;
313 161
 
314 162
 	/* Initialise DHCP packet content */
@@ -327,180 +175,19 @@ static int create_dhcp_packet ( struct net_device *netdev, uint8_t msgtype,
327 175
 	}
328 176
 	dhcphdr->hlen = hlen;
329 177
 	memcpy ( dhcphdr->chaddr, netdev->ll_addr, hlen );
178
+	memcpy ( dhcphdr->options, options, options_len );
330 179
 
331
-	/* Initialise DHCP packet structure */
332
-	dhcppkt->dhcphdr = dhcphdr;
333
-	dhcppkt->max_len = max_len;
334
-	init_dhcp_options ( &dhcppkt->options, dhcphdr->options,
335
-			    ( max_len -
336
-			      offsetof ( typeof ( *dhcphdr ), options ) ) );
180
+	/* Initialise DHCP packet structure and settings interface */
181
+	dhcppkt_init ( dhcppkt, NULL, data, max_len );
337 182
 	
338 183
 	/* Set DHCP_MESSAGE_TYPE option */
339
-	if ( ( rc = set_dhcp_packet_option ( dhcppkt, DHCP_MESSAGE_TYPE,
340
-					     &msgtype,
341
-					     sizeof ( msgtype ) ) ) != 0 )
184
+	if ( ( rc = store_setting ( &dhcppkt->settings, DHCP_MESSAGE_TYPE,
185
+				    &msgtype, sizeof ( msgtype ) ) ) != 0 )
342 186
 		return rc;
343 187
 
344 188
 	return 0;
345 189
 }
346 190
 
347
-/**
348
- * Calculate used length of a field containing DHCP options
349
- *
350
- * @v data		Field containing DHCP options
351
- * @v max_len		Field length
352
- * @ret len		Used length (excluding the @c DHCP_END tag)
353
- */
354
-static size_t dhcp_field_len ( const void *data, size_t max_len ) {
355
-	struct dhcp_option_block options;
356
-	struct dhcp_option *end;
357
-
358
-	options.data = ( ( void * ) data );
359
-	options.len = max_len;
360
-	end = find_dhcp_option ( &options, DHCP_END );
361
-	return ( end ? ( ( ( void * ) end ) - data ) : 0 );
362
-}
363
-
364
-/**
365
- * Merge field containing DHCP options or string into DHCP options block
366
- *
367
- * @v options		DHCP option block
368
- * @v data		Field containing DHCP options
369
- * @v max_len		Field length
370
- * @v tag		DHCP option tag, or 0
371
- *
372
- * If @c tag is non-zero (and the field is not empty), the field will
373
- * be treated as a NUL-terminated string representing the value of the
374
- * specified DHCP option.  If @c tag is zero, the field will be
375
- * treated as a block of DHCP options, and simply appended to the
376
- * existing options in the option block.
377
- *
378
- * The caller must ensure that there is enough space in the options
379
- * block to perform the merge.
380
- */
381
-static void merge_dhcp_field ( struct dhcp_option_block *options,
382
-			       const void *data, size_t max_len,
383
-			       unsigned int tag ) {
384
-	size_t len;
385
-	void *dest;
386
-	struct dhcp_option *end;
387
-
388
-	if ( tag ) {
389
-		len = strlen ( data );
390
-		if ( len )
391
-			set_dhcp_option ( options, tag, data, len );
392
-	} else {
393
-		len = dhcp_field_len ( data, max_len );
394
-		dest = ( options->data + options->len - 1 );
395
-		memcpy ( dest, data, len );
396
-		options->len += len;
397
-		end = ( dest + len );
398
-		end->tag = DHCP_END;
399
-	}
400
-}
401
-
402
-/**
403
- * Parse DHCP packet and construct DHCP options block
404
- *
405
- * @v dhcphdr		DHCP packet
406
- * @v len		Length of DHCP packet
407
- * @ret options		DHCP options block, or NULL
408
- *
409
- * Parses a received DHCP packet and canonicalises its contents into a
410
- * single DHCP options block.  The "file" and "sname" fields are
411
- * converted into the corresponding DHCP options (@c
412
- * DHCP_BOOTFILE_NAME and @c DHCP_TFTP_SERVER_NAME respectively).  If
413
- * these fields are used for option overloading, their options are
414
- * merged in to the options block.
415
- *
416
- * The values of the "yiaddr" and "siaddr" fields will be stored
417
- * within the options block as the magic options @c DHCP_EB_YIADDR and
418
- * @c DHCP_EB_SIADDR.
419
- * 
420
- * Note that this call allocates new memory for the constructed DHCP
421
- * options block; it is the responsibility of the caller to eventually
422
- * free this memory.
423
- */
424
-static struct dhcp_option_block * dhcp_parse ( const struct dhcphdr *dhcphdr,
425
-					       size_t len ) {
426
-	struct dhcp_option_block *options;
427
-	size_t options_len;
428
-	unsigned int overloading;
429
-
430
-	/* Sanity check */
431
-	if ( len < sizeof ( *dhcphdr ) )
432
-		return NULL;
433
-
434
-	/* Calculate size of resulting concatenated option block:
435
-	 *
436
-	 *   The "options" field : length of the field minus the DHCP_END tag.
437
-	 *
438
-	 *   The "file" field : maximum length of the field minus the
439
-	 *   NUL terminator, plus a 2-byte DHCP header or, if used for
440
-	 *   option overloading, the length of the field minus the
441
-	 *   DHCP_END tag.
442
-	 *
443
-	 *   The "sname" field : as for the "file" field.
444
-	 *
445
-	 *   15 bytes for an encapsulated options field to contain the
446
-	 *   value of the "yiaddr" and "siaddr" fields
447
-	 *
448
-	 *   1 byte for a final terminating DHCP_END tag.
449
-	 */
450
-	options_len = ( ( len - offsetof ( typeof ( *dhcphdr ), options ) ) - 1
451
-			+ ( sizeof ( dhcphdr->file ) + 1 )
452
-			+ ( sizeof ( dhcphdr->sname ) + 1 )
453
-			+ 15 /* yiaddr and siaddr */
454
-			+ 1 /* DHCP_END tag */ );
455
-	
456
-	/* Allocate empty options block of required size */
457
-	options = alloc_dhcp_options ( options_len );
458
-	if ( ! options ) {
459
-		DBG ( "DHCP could not allocate %zd-byte option block\n",
460
-		      options_len );
461
-		return NULL;
462
-	}
463
-	
464
-	/* Merge in "options" field, if this is a DHCP packet */
465
-	if ( dhcphdr->magic == htonl ( DHCP_MAGIC_COOKIE ) ) {
466
-		merge_dhcp_field ( options, dhcphdr->options,
467
-				   ( len -
468
-				     offsetof ( typeof (*dhcphdr), options ) ),
469
-				   0 /* Always contains options */ );
470
-	}
471
-
472
-	/* Identify overloaded fields */
473
-	overloading = find_dhcp_num_option ( options, DHCP_OPTION_OVERLOAD );
474
-	
475
-	/* Merge in "file" and "sname" fields */
476
-	merge_dhcp_field ( options, dhcphdr->file, sizeof ( dhcphdr->file ),
477
-			   ( ( overloading & DHCP_OPTION_OVERLOAD_FILE ) ?
478
-			     0 : DHCP_BOOTFILE_NAME ) );
479
-	merge_dhcp_field ( options, dhcphdr->sname, sizeof ( dhcphdr->sname ),
480
-			   ( ( overloading & DHCP_OPTION_OVERLOAD_SNAME ) ?
481
-			     0 : DHCP_TFTP_SERVER_NAME ) );
482
-
483
-	/* Set magic options for "yiaddr" and "siaddr", if present */
484
-	if ( dhcphdr->yiaddr.s_addr ) {
485
-		set_dhcp_option ( options, DHCP_EB_YIADDR,
486
-				  &dhcphdr->yiaddr, sizeof (dhcphdr->yiaddr) );
487
-	}
488
-	if ( dhcphdr->siaddr.s_addr ) {
489
-		set_dhcp_option ( options, DHCP_EB_SIADDR,
490
-				  &dhcphdr->siaddr, sizeof (dhcphdr->siaddr) );
491
-	}
492
-	
493
-	assert ( options->len <= options->max_len );
494
-
495
-	return options;
496
-}
497
-
498
-/****************************************************************************
499
- *
500
- * Whole-packet construction
501
- *
502
- */
503
-
504 191
 /** DHCP network device descriptor */
505 192
 struct dhcp_netdev_desc {
506 193
 	/** Bus type ID */
@@ -532,18 +219,18 @@ struct dhcp_client_uuid {
532 219
 /**
533 220
  * Create DHCP request
534 221
  *
222
+ * @v dhcppkt		DHCP packet structure to fill in
535 223
  * @v netdev		Network device
536 224
  * @v msgtype		DHCP message type
537
- * @v options		DHCP server response options, or NULL
225
+ * @v offer_settings	Settings received in DHCPOFFER, or NULL
538 226
  * @v data		Buffer for DHCP packet
539 227
  * @v max_len		Size of DHCP packet buffer
540
- * @v dhcppkt		DHCP packet structure to fill in
541 228
  * @ret rc		Return status code
542 229
  */
543
-int create_dhcp_request ( struct net_device *netdev, int msgtype,
544
-			  struct dhcp_option_block *options,
545
-			  void *data, size_t max_len,
546
-			  struct dhcp_packet *dhcppkt ) {
230
+int create_dhcp_request ( struct dhcp_packet *dhcppkt,
231
+			  struct net_device *netdev, int msgtype,
232
+			  struct settings *offer_settings,
233
+			  void *data, size_t max_len ) {
547 234
 	struct device_description *desc = &netdev->dev->desc;
548 235
 	struct dhcp_netdev_desc dhcp_desc;
549 236
 	struct dhcp_client_id client_id;
@@ -553,33 +240,27 @@ int create_dhcp_request ( struct net_device *netdev, int msgtype,
553 240
 	int rc;
554 241
 
555 242
 	/* Create DHCP packet */
556
-	if ( ( rc = create_dhcp_packet ( netdev, msgtype, data, max_len,
557
-					 dhcppkt ) ) != 0 ) {
243
+	if ( ( rc = create_dhcp_packet ( dhcppkt, netdev, msgtype,
244
+					 &dhcp_request_options, data,
245
+					 max_len ) ) != 0 ) {
558 246
 		DBG ( "DHCP could not create DHCP packet: %s\n",
559 247
 		      strerror ( rc ) );
560 248
 		return rc;
561 249
 	}
562 250
 
563
-	/* Copy in options common to all requests */
564
-	if ( ( rc = copy_dhcp_packet_options ( dhcppkt,
565
-					       &dhcp_request_options )) !=0 ){
566
-		DBG ( "DHCP could not set common DHCP options: %s\n",
567
-		      strerror ( rc ) );
568
-		return rc;
569
-	}
570
-
571 251
 	/* Copy any required options from previous server repsonse */
572
-	if ( options ) {
573
-		if ( ( rc = copy_dhcp_packet_option ( dhcppkt, options,
574
-					  DHCP_SERVER_IDENTIFIER,
575
-					  DHCP_SERVER_IDENTIFIER ) ) != 0 ) {
252
+	if ( offer_settings ) {
253
+		if ( ( rc = copy_setting ( &dhcppkt->settings,
254
+					   DHCP_SERVER_IDENTIFIER,
255
+					   offer_settings,
256
+					   DHCP_SERVER_IDENTIFIER ) ) != 0 ) {
576 257
 			DBG ( "DHCP could not set server identifier "
577 258
 			      "option: %s\n", strerror ( rc ) );
578 259
 			return rc;
579 260
 		}
580
-		if ( ( rc = copy_dhcp_packet_option ( dhcppkt, options,
581
-					  DHCP_EB_YIADDR,
582
-					  DHCP_REQUESTED_ADDRESS ) ) != 0 ) {
261
+		if ( ( rc = copy_setting ( &dhcppkt->settings, DHCP_EB_YIADDR,
262
+					   offer_settings,
263
+					   DHCP_REQUESTED_ADDRESS ) ) != 0 ) {
583 264
 			DBG ( "DHCP could not set requested address "
584 265
 			      "option: %s\n", strerror ( rc ) );
585 266
 			return rc;
@@ -588,9 +269,8 @@ int create_dhcp_request ( struct net_device *netdev, int msgtype,
588 269
 
589 270
 	/* Add options to identify the feature list */
590 271
 	dhcp_features_len = ( dhcp_features_end - dhcp_features );
591
-	if ( ( rc = set_dhcp_packet_option ( dhcppkt, DHCP_EB_ENCAP,
592
-					     dhcp_features,
593
-					     dhcp_features_len ) ) != 0 ) {
272
+	if ( ( rc = store_setting ( &dhcppkt->settings, DHCP_EB_ENCAP,
273
+				    dhcp_features, dhcp_features_len ) ) !=0 ){
594 274
 		DBG ( "DHCP could not set features list option: %s\n",
595 275
 		      strerror ( rc ) );
596 276
 		return rc;
@@ -600,9 +280,8 @@ int create_dhcp_request ( struct net_device *netdev, int msgtype,
600 280
 	dhcp_desc.type = desc->bus_type;
601 281
 	dhcp_desc.vendor = htons ( desc->vendor );
602 282
 	dhcp_desc.device = htons ( desc->device );
603
-	if ( ( rc = set_dhcp_packet_option ( dhcppkt, DHCP_EB_BUS_ID,
604
-					     &dhcp_desc,
605
-					     sizeof ( dhcp_desc ) ) ) != 0 ) {
283
+	if ( ( rc = store_setting ( &dhcppkt->settings, DHCP_EB_BUS_ID,
284
+				    &dhcp_desc, sizeof ( dhcp_desc ) ) ) !=0 ){
606 285
 		DBG ( "DHCP could not set bus ID option: %s\n",
607 286
 		      strerror ( rc ) );
608 287
 		return rc;
@@ -615,9 +294,8 @@ int create_dhcp_request ( struct net_device *netdev, int msgtype,
615 294
 	ll_addr_len = netdev->ll_protocol->ll_addr_len;
616 295
 	assert ( ll_addr_len <= sizeof ( client_id.ll_addr ) );
617 296
 	memcpy ( client_id.ll_addr, netdev->ll_addr, ll_addr_len );
618
-	if ( ( rc = set_dhcp_packet_option ( dhcppkt, DHCP_CLIENT_ID,
619
-					     &client_id,
620
-					     ( ll_addr_len + 1 ) ) ) != 0 ) {
297
+	if ( ( rc = store_setting ( &dhcppkt->settings, DHCP_CLIENT_ID,
298
+				    &client_id, ( ll_addr_len + 1 ) ) ) != 0 ){
621 299
 		DBG ( "DHCP could not set client ID: %s\n",
622 300
 		      strerror ( rc ) );
623 301
 		return rc;
@@ -626,9 +304,9 @@ int create_dhcp_request ( struct net_device *netdev, int msgtype,
626 304
 	/* Add client UUID, if we have one.  Required for PXE. */
627 305
 	client_uuid.type = DHCP_CLIENT_UUID_TYPE;
628 306
 	if ( ( rc = get_uuid ( &client_uuid.uuid ) ) == 0 ) {
629
-		if ( ( rc = set_dhcp_packet_option ( dhcppkt,
630
-					   DHCP_CLIENT_UUID, &client_uuid,
631
-					   sizeof ( client_uuid ) ) ) != 0 ) {
307
+		if ( ( rc = store_setting ( &dhcppkt->settings,
308
+					    DHCP_CLIENT_UUID, &client_uuid,
309
+					    sizeof ( client_uuid ) ) ) != 0 ) {
632 310
 			DBG ( "DHCP could not set client UUID: %s\n",
633 311
 			      strerror ( rc ) );
634 312
 			return rc;
@@ -641,34 +319,86 @@ int create_dhcp_request ( struct net_device *netdev, int msgtype,
641 319
 /**
642 320
  * Create DHCP response
643 321
  *
322
+ * @v dhcppkt		DHCP packet structure to fill in
644 323
  * @v netdev		Network device
645 324
  * @v msgtype		DHCP message type
646
- * @v options		DHCP options, or NULL
325
+ * @v settings		Settings to include, or NULL
647 326
  * @v data		Buffer for DHCP packet
648 327
  * @v max_len		Size of DHCP packet buffer
649
- * @v dhcppkt		DHCP packet structure to fill in
650 328
  * @ret rc		Return status code
651 329
  */
652
-int create_dhcp_response ( struct net_device *netdev, int msgtype,
653
-			   struct dhcp_option_block *options,
654
-			   void *data, size_t max_len,
655
-			   struct dhcp_packet *dhcppkt ) {
330
+int create_dhcp_response ( struct dhcp_packet *dhcppkt,
331
+			   struct net_device *netdev, int msgtype,
332
+			   struct settings *settings,
333
+			   void *data, size_t max_len ) {
656 334
 	int rc;
657 335
 
658 336
 	/* Create packet and copy in options */
659
-	if ( ( rc = create_dhcp_packet ( netdev, msgtype, data, max_len,
660
-					 dhcppkt ) ) != 0 ) {
661
-		DBG ( " failed to build packet" );
337
+	if ( ( rc = create_dhcp_packet ( dhcppkt, netdev, msgtype, NULL,
338
+					 data, max_len ) ) != 0 )
662 339
 		return rc;
663
-	}
664
-	if ( ( rc = copy_dhcp_packet_options ( dhcppkt, options ) ) != 0 ) {
665
-		DBG ( " failed to copy options" );
340
+	if ( ( rc = copy_settings ( &dhcppkt->settings, settings ) ) != 0 )
666 341
 		return rc;
667
-	}
668 342
 
669 343
 	return 0;
670 344
 }
671 345
 
346
+/****************************************************************************
347
+ *
348
+ * DHCP packets contained in I/O buffers
349
+ *
350
+ */
351
+
352
+/** A DHCP packet contained in an I/O buffer */
353
+struct dhcp_iobuf_packet {
354
+	/** Reference counter */
355
+	struct refcnt refcnt;
356
+	/** DHCP packet */
357
+	struct dhcp_packet dhcppkt;
358
+	/** Containing I/O buffer */
359
+	struct io_buffer *iobuf;
360
+};
361
+
362
+/**
363
+ * Free DHCP packet contained in an I/O buffer
364
+ *
365
+ * @v refcnt		Reference counter
366
+ */
367
+static void dhcpiob_free ( struct refcnt *refcnt ) {
368
+	struct dhcp_iobuf_packet *dhcpiob =
369
+		container_of ( refcnt, struct dhcp_iobuf_packet, refcnt );
370
+
371
+	free_iob ( dhcpiob->iobuf );
372
+	free ( dhcpiob );
373
+}
374
+
375
+/**
376
+ * Create DHCP packet from I/O buffer
377
+ *
378
+ * @v iobuf		I/O buffer
379
+ * @ret dhcpiob		DHCP packet contained in I/O buffer
380
+ *
381
+ * This function takes ownership of the I/O buffer.  Future accesses
382
+ * must be via the @c dhcpiob data structure.
383
+ */
384
+static struct dhcp_iobuf_packet * dhcpiob_create ( struct io_buffer *iobuf ) {
385
+	struct dhcp_iobuf_packet *dhcpiob;
386
+
387
+	dhcpiob = zalloc ( sizeof ( *dhcpiob ) );
388
+	if ( dhcpiob ) {
389
+		dhcpiob->refcnt.free = dhcpiob_free;
390
+		dhcpiob->iobuf = iobuf;
391
+		dhcppkt_init ( &dhcpiob->dhcppkt, &dhcpiob->refcnt,
392
+			       iobuf->data, iob_len ( iobuf ) );
393
+	}
394
+	return dhcpiob;
395
+}
396
+
397
+static void dhcpiob_put ( struct dhcp_iobuf_packet *dhcpiob ) {
398
+	if ( dhcpiob )
399
+		ref_put ( &dhcpiob->refcnt );
400
+}
401
+
672 402
 /****************************************************************************
673 403
  *
674 404
  * DHCP to UDP interface
@@ -686,9 +416,6 @@ struct dhcp_session {
686 416
 
687 417
 	/** Network device being configured */
688 418
 	struct net_device *netdev;
689
-	/** Option block registration routine */
690
-	int ( * register_options ) ( struct net_device *netdev,
691
-				     struct dhcp_option_block *options );
692 419
 
693 420
 	/** State of the session
694 421
 	 *
@@ -696,10 +423,10 @@ struct dhcp_session {
696 423
 	 * (e.g. @c DHCPDISCOVER).
697 424
 	 */
698 425
 	int state;
699
-	/** Options obtained from DHCP server */
700
-	struct dhcp_option_block *options;
701
-	/** Options obtained from ProxyDHCP server */
702
-	struct dhcp_option_block *proxy_options;
426
+	/** Response obtained from DHCP server */
427
+	struct dhcp_iobuf_packet *response;
428
+	/** Response obtained from ProxyDHCP server */
429
+	struct dhcp_iobuf_packet *proxy_response;
703 430
 	/** Retransmission timer */
704 431
 	struct retry_timer timer;
705 432
 	/** Session start time (in ticks) */
@@ -716,8 +443,8 @@ static void dhcp_free ( struct refcnt *refcnt ) {
716 443
 		container_of ( refcnt, struct dhcp_session, refcnt );
717 444
 
718 445
 	netdev_put ( dhcp->netdev );
719
-	dhcpopt_put ( dhcp->options );
720
-	dhcpopt_put ( dhcp->proxy_options );
446
+	dhcpiob_put ( dhcp->response );
447
+	dhcpiob_put ( dhcp->proxy_response );
721 448
 	free ( dhcp );
722 449
 }
723 450
 
@@ -741,6 +468,31 @@ static void dhcp_finished ( struct dhcp_session *dhcp, int rc ) {
741 468
 	job_done ( &dhcp->job, rc );
742 469
 }
743 470
 
471
+/**
472
+ * Register options received via DHCP
473
+ *
474
+ * @v dhcp		DHCP session
475
+ * @ret rc		Return status code
476
+ */
477
+static int dhcp_register_settings ( struct dhcp_session *dhcp ) {
478
+	struct settings *settings;
479
+	struct settings *parent;
480
+	int rc;
481
+
482
+	if ( dhcp->proxy_response ) {
483
+		settings = &dhcp->proxy_response->dhcppkt.settings;
484
+		if ( ( rc = register_settings ( settings, NULL ) ) != 0 )
485
+			return rc;
486
+	}
487
+
488
+	settings = &dhcp->response->dhcppkt.settings;
489
+	parent = netdev_settings ( dhcp->netdev );
490
+	if ( ( rc = register_settings ( settings, parent ) ) != 0 )
491
+		return rc;
492
+
493
+	return 0;
494
+}
495
+
744 496
 /****************************************************************************
745 497
  *
746 498
  * Data transfer interface
@@ -757,6 +509,7 @@ static int dhcp_send_request ( struct dhcp_session *dhcp ) {
757 509
 	struct xfer_metadata meta = {
758 510
 		.netdev = dhcp->netdev,
759 511
 	};
512
+	struct settings *offer_settings = NULL;
760 513
 	struct io_buffer *iobuf;
761 514
 	struct dhcp_packet dhcppkt;
762 515
 	int rc;
@@ -778,10 +531,11 @@ static int dhcp_send_request ( struct dhcp_session *dhcp ) {
778 531
 		return -ENOMEM;
779 532
 
780 533
 	/* Create DHCP packet in temporary buffer */
781
-	if ( ( rc = create_dhcp_request ( dhcp->netdev, dhcp->state,
782
-					  dhcp->options, iobuf->data,
783
-					  iob_tailroom ( iobuf ),
784
-					  &dhcppkt ) ) != 0 ) {
534
+	if ( dhcp->response )
535
+		offer_settings = &dhcp->response->dhcppkt.settings;
536
+	if ( ( rc = create_dhcp_request ( &dhcppkt, dhcp->netdev, dhcp->state,
537
+					  offer_settings, iobuf->data,
538
+					  iob_tailroom ( iobuf ) ) ) != 0 ) {
785 539
 		DBGC ( dhcp, "DHCP %p could not construct DHCP request: %s\n",
786 540
 		       dhcp, strerror ( rc ) );
787 541
 		goto done;
@@ -828,36 +582,41 @@ static void dhcp_timer_expired ( struct retry_timer *timer, int fail ) {
828 582
  * @v len		Length of received data
829 583
  * @ret rc		Return status code
830 584
  */
831
-static int dhcp_deliver_raw ( struct xfer_interface *xfer,
832
-			      const void *data, size_t len ) {
585
+static int dhcp_deliver_iob ( struct xfer_interface *xfer,
586
+			      struct io_buffer *iobuf,
587
+			      struct xfer_metadata *meta __unused ) {
833 588
 	struct dhcp_session *dhcp =
834 589
 		container_of ( xfer, struct dhcp_session, xfer );
835
-	const struct dhcphdr *dhcphdr = data;
836
-	struct dhcp_option_block *options;
837
-	struct dhcp_option_block **store_options;
590
+	struct dhcp_iobuf_packet *response;
591
+	struct dhcp_iobuf_packet **store_response;
592
+	struct dhcphdr *dhcphdr;
593
+	struct settings *settings;
838 594
 	unsigned int msgtype;
839 595
 	unsigned long elapsed;
840 596
 	int is_proxy;
841 597
 	int ignore_proxy;
598
+	int rc;
599
+
600
+	/* Convert packet into a DHCP-packet-in-iobuf */
601
+	response = dhcpiob_create ( iobuf );
602
+	if ( ! response ) {
603
+		DBGC ( dhcp, "DHCP %p could not store DHCP packet\n", dhcp );
604
+		return -ENOMEM;
605
+	}
606
+	dhcphdr = response->dhcppkt.dhcphdr;
607
+	settings = &response->dhcppkt.settings;
842 608
 
843 609
 	/* Check for matching transaction ID */
844 610
 	if ( dhcphdr->xid != dhcp_xid ( dhcp->netdev ) ) {
845 611
 		DBGC ( dhcp, "DHCP %p wrong transaction ID (wanted %08lx, "
846 612
 			"got %08lx)\n", dhcp, ntohl ( dhcphdr->xid ),
847 613
 			ntohl ( dhcp_xid ( dhcp->netdev ) ) );
848
-		return 0;
849
-	};
850
-
851
-	/* Parse packet and create options structure */
852
-	options = dhcp_parse ( dhcphdr, len );
853
-	if ( ! options ) {
854
-		DBGC ( dhcp, "DHCP %p could not parse DHCP packet\n", dhcp );
855
-		return -EINVAL;
856
-	}
614
+		goto out_discard;
615
+	};	
857 616
 
858 617
 	/* Determine and verify message type */
859 618
 	is_proxy = ( dhcphdr->yiaddr.s_addr == 0 );
860
-	msgtype = find_dhcp_num_option ( options, DHCP_MESSAGE_TYPE );
619
+	msgtype = fetch_uintz_setting ( settings, DHCP_MESSAGE_TYPE );
861 620
 	DBGC ( dhcp, "DHCP %p received %s%s\n", dhcp,
862 621
 	       ( is_proxy ? "Proxy" : "" ), dhcp_msgtype_name ( msgtype ) );
863 622
 	if ( ( ( dhcp->state != DHCPDISCOVER ) || ( msgtype != DHCPOFFER ) ) &&
@@ -872,25 +631,26 @@ static int dhcp_deliver_raw ( struct xfer_interface *xfer,
872 631
 	 * options have equal or higher priority than the
873 632
 	 * currently-stored options.
874 633
 	 */
875
-	store_options = ( is_proxy ? &dhcp->proxy_options : &dhcp->options );
876
-	if ( ( ! *store_options ) || 
877
-	     ( find_dhcp_num_option ( options, DHCP_EB_PRIORITY ) >=
878
-	       find_dhcp_num_option ( *store_options, DHCP_EB_PRIORITY ) ) ) {
879
-		dhcpopt_put ( *store_options );
880
-		*store_options = options;
634
+	store_response = ( is_proxy ? &dhcp->proxy_response : &dhcp->response);
635
+	if ( ( ! *store_response ) || 
636
+	     ( fetch_uintz_setting ( settings, DHCP_EB_PRIORITY ) >=
637
+	       fetch_uintz_setting ( &(*store_response)->dhcppkt.settings,
638
+				     DHCP_EB_PRIORITY ) ) ) {
639
+		dhcpiob_put ( *store_response );
640
+		*store_response = response;
881 641
 	} else {
882
-		dhcpopt_put ( options );
642
+		dhcpiob_put ( response );
883 643
 	}
884 644
 
885 645
 	/* If we don't yet have a standard DHCP response (i.e. one
886 646
 	 * with an IP address), then just leave the timer running.
887 647
 	 */
888
-	if ( ! dhcp->options )
648
+	if ( ! dhcp->response )
889 649
 		goto out;
890 650
 
891 651
 	/* Handle DHCP response */
892
-	ignore_proxy = find_dhcp_num_option ( dhcp->options,
893
-					      DHCP_EB_NO_PROXYDHCP );
652
+	ignore_proxy = fetch_uintz_setting ( &dhcp->response->dhcppkt.settings,
653
+					     DHCP_EB_NO_PROXYDHCP );
894 654
 	switch ( dhcp->state ) {
895 655
 	case DHCPDISCOVER:
896 656
 		/* If we have allowed sufficient time for ProxyDHCP
@@ -905,11 +665,14 @@ static int dhcp_deliver_raw ( struct xfer_interface *xfer,
905 665
 		break;
906 666
 	case DHCPREQUEST:
907 667
 		/* DHCP finished; register options and exit */
908
-		if ( dhcp->proxy_options && ( ! ignore_proxy ) ) {
909
-			dhcp->register_options ( dhcp->netdev,
910
-						 dhcp->proxy_options );
668
+		if ( ignore_proxy && dhcp->proxy_response ) {
669
+			dhcpiob_put ( dhcp->proxy_response );
670
+			dhcp->proxy_response = NULL;
671
+		}
672
+		if ( ( rc = dhcp_register_settings ( dhcp ) ) != 0 ) {
673
+			dhcp_finished ( dhcp, rc );
674
+			break;
911 675
 		}
912
-		dhcp->register_options ( dhcp->netdev, dhcp->options );
913 676
 		dhcp_finished ( dhcp, 0 );
914 677
 		break;
915 678
 	default:
@@ -920,7 +683,7 @@ static int dhcp_deliver_raw ( struct xfer_interface *xfer,
920 683
 	return 0;
921 684
 
922 685
  out_discard:
923
-	dhcpopt_put ( options );
686
+	dhcpiob_put ( response );
924 687
 	return 0;
925 688
 }
926 689
 
@@ -930,8 +693,8 @@ static struct xfer_interface_operations dhcp_xfer_operations = {
930 693
 	.vredirect	= xfer_vopen,
931 694
 	.window		= unlimited_xfer_window,
932 695
 	.alloc_iob	= default_xfer_alloc_iob,
933
-	.deliver_iob	= xfer_deliver_as_raw,
934
-	.deliver_raw	= dhcp_deliver_raw,
696
+	.deliver_iob	= dhcp_deliver_iob,
697
+	.deliver_raw	= xfer_deliver_as_iob,
935 698
 };
936 699
 
937 700
 /****************************************************************************
@@ -978,9 +741,7 @@ static struct job_interface_operations dhcp_job_operations = {
978 741
  * register_options() routine will be called with the acquired
979 742
  * options.
980 743
  */
981
-int start_dhcp ( struct job_interface *job, struct net_device *netdev,
982
-		 int ( * register_options ) ( struct net_device *netdev,
983
-					      struct dhcp_option_block * ) ) {
744
+int start_dhcp ( struct job_interface *job, struct net_device *netdev ) {
984 745
 	static struct sockaddr_in server = {
985 746
 		.sin_family = AF_INET,
986 747
 		.sin_addr.s_addr = INADDR_BROADCAST,
@@ -1001,7 +762,6 @@ int start_dhcp ( struct job_interface *job, struct net_device *netdev,
1001 762
 	job_init ( &dhcp->job, &dhcp_job_operations, &dhcp->refcnt );
1002 763
 	xfer_init ( &dhcp->xfer, &dhcp_xfer_operations, &dhcp->refcnt );
1003 764
 	dhcp->netdev = netdev_get ( netdev );
1004
-	dhcp->register_options = register_options;
1005 765
 	dhcp->timer.expired = dhcp_timer_expired;
1006 766
 	dhcp->state = DHCPDISCOVER;
1007 767
 	dhcp->start = currticks();

+ 5
- 18
src/usr/dhcpmgmt.c Целия файл

@@ -32,16 +32,8 @@
32 32
  *
33 33
  */
34 34
 
35
-static int dhcp_success ( struct net_device *netdev __unused,
36
-			  struct dhcp_option_block *options ) {
37
-	DBGC ( options, "DHCP client registering options %p\n", options );
38
-	register_dhcp_options ( options );
39
-	return 0;
40
-}
41
-
42 35
 int dhcp ( struct net_device *netdev ) {
43
-	struct dhcp_option_block *options;
44
-	struct dhcp_option_block *tmp;
36
+	struct settings *settings;
45 37
 	int rc;
46 38
 
47 39
 	/* Check we can open the interface first */
@@ -49,18 +41,13 @@ int dhcp ( struct net_device *netdev ) {
49 41
 		return rc;
50 42
 
51 43
 	/* Unregister any option blocks acquired via DHCP */
52
-	list_for_each_entry_safe ( options, tmp, &dhcp_option_blocks, list ) {
53
-		/* Skip static option blocks (e.g. from NVS) */
54
-		if ( find_dhcp_option ( options, DHCP_MESSAGE_TYPE ) ) {
55
-			DBGC ( options, "DHCP client unregistering options "
56
-			       "%p\n", options );
57
-			unregister_dhcp_options ( options );
58
-		}
59
-	}
44
+	settings = find_child_settings ( netdev_settings ( netdev ), "dhcp" );
45
+	if ( settings )
46
+		unregister_settings ( settings );
60 47
 
61 48
 	/* Perform DHCP */
62 49
 	printf ( "DHCP (%s %s)", netdev->name, netdev_hwaddr ( netdev ) );
63
-	if ( ( rc = start_dhcp ( &monojob, netdev, dhcp_success ) ) == 0 )
50
+	if ( ( rc = start_dhcp ( &monojob, netdev ) ) == 0 )
64 51
 		rc = monojob_wait ( "" );
65 52
 
66 53
 	return rc;

Loading…
Отказ
Запис