Browse Source

Add sketch code to reassemble a DHCP packet from our internal "everything

is a DHCP option" data structures.

We need this code in order to be able to return a DHCP packet to a PXE NBP
which reflects options from our multiple sources (e.g. NVS and DHCP
server).  This is expensive, but necessary.  Having paid this cost, we may
as well try to use the same code to generate our DHCP request packets,
since the process is similar.
tags/v0.9.3
Michael Brown 18 years ago
parent
commit
b24947f0c0
4 changed files with 243 additions and 8 deletions
  1. 1
    0
      src/include/errno.h
  2. 22
    0
      src/include/gpxe/dhcp.h
  3. 24
    8
      src/net/dhcpopts.c
  4. 196
    0
      src/net/udp/dhcp.c

+ 1
- 0
src/include/errno.h View File

151
 #define ENOENT		0xe8	/**< No such file or directory */
151
 #define ENOENT		0xe8	/**< No such file or directory */
152
 #define ENOEXEC		0xe9	/**< Exec format error */
152
 #define ENOEXEC		0xe9	/**< Exec format error */
153
 #define ENOMSG		ENODATA	/**< No message of the desired type */
153
 #define ENOMSG		ENODATA	/**< No message of the desired type */
154
+#define ENOSPC		ENOMEM	/**< No space left on device */
154
 #define ENOSR		0xea	/**< No stream resources */
155
 #define ENOSR		0xea	/**< No stream resources */
155
 #define ENOSTR		0xeb	/**< Not a stream */
156
 #define ENOSTR		0xeb	/**< Not a stream */
156
 #define ENOSYS		0xec	/**< Function not implemented */
157
 #define ENOSYS		0xec	/**< Function not implemented */

+ 22
- 0
src/include/gpxe/dhcp.h View File

118
  */
118
  */
119
 #define DHCP_PAD 0
119
 #define DHCP_PAD 0
120
 
120
 
121
+/** Minimum normal DHCP option */
122
+#define DHCP_MIN_OPTION 1
123
+
124
+/** Vendor encapsulated options */
125
+#define DHCP_VENDOR_ENCAP 43
126
+
121
 /** Option overloading
127
 /** Option overloading
122
  *
128
  *
123
  * The value of this option is the bitwise-OR of zero or more
129
  * The value of this option is the bitwise-OR of zero or more
131
 /** The "sname" field is overloaded to contain extra DHCP options */
137
 /** The "sname" field is overloaded to contain extra DHCP options */
132
 #define DHCP_OPTION_OVERLOAD_SNAME 2
138
 #define DHCP_OPTION_OVERLOAD_SNAME 2
133
 
139
 
140
+/** DHCP message type */
141
+#define DHCP_MESSAGE_TYPE 53
142
+#define DHCPDISCOVER 1
143
+#define DHCPOFFER 2
144
+#define DHCPREQUEST 3
145
+#define DHCPDECLINE 4
146
+#define DHCPACK 5
147
+#define DHCPNAK 6
148
+#define DHCPRELEASE 7
149
+#define DHCPINFORM 8
150
+
134
 /** TFTP server name
151
 /** TFTP server name
135
  *
152
  *
136
  * This option replaces the fixed "sname" field, when that field is
153
  * This option replaces the fixed "sname" field, when that field is
178
  */
195
  */
179
 #define DHCP_EB_SIADDR DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 3 )
196
 #define DHCP_EB_SIADDR DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 3 )
180
 
197
 
198
+/** Maximum normal DHCP option */
199
+#define DHCP_MAX_OPTION 254
200
+
181
 /** End of options
201
 /** End of options
182
  *
202
  *
183
  * This tag does not have a length field; it is always only a single
203
  * This tag does not have a length field; it is always only a single
260
 extern struct dhcp_option * find_global_dhcp_option ( unsigned int tag );
280
 extern struct dhcp_option * find_global_dhcp_option ( unsigned int tag );
261
 extern void register_dhcp_options ( struct dhcp_option_block *options );
281
 extern void register_dhcp_options ( struct dhcp_option_block *options );
262
 extern void unregister_dhcp_options ( struct dhcp_option_block *options );
282
 extern void unregister_dhcp_options ( struct dhcp_option_block *options );
283
+extern void init_dhcp_options ( struct dhcp_option_block *options,
284
+				void *data, size_t max_len );
263
 extern struct dhcp_option_block * alloc_dhcp_options ( size_t max_len );
285
 extern struct dhcp_option_block * alloc_dhcp_options ( size_t max_len );
264
 extern void free_dhcp_options ( struct dhcp_option_block *options );
286
 extern void free_dhcp_options ( struct dhcp_option_block *options );
265
 extern struct dhcp_option *
287
 extern struct dhcp_option *

+ 24
- 8
src/net/dhcpopts.c View File

227
 	list_del ( &options->list );
227
 	list_del ( &options->list );
228
 }
228
 }
229
 
229
 
230
+/**
231
+ * Initialise empty block of DHCP options
232
+ *
233
+ * @v options		Uninitialised DHCP option block
234
+ * @v data		Memory for DHCP option data
235
+ * @v max_len		Length of memory for DHCP option data
236
+ *
237
+ * Populates the DHCP option data with a single @c DHCP_END option and
238
+ * fills in the fields of the @c dhcp_option_block structure.
239
+ */
240
+void init_dhcp_options ( struct dhcp_option_block *options,
241
+			 void *data, size_t max_len ) {
242
+	struct dhcp_option *option;
243
+
244
+	options->data = data;
245
+	options->max_len = max_len;
246
+	option = options->data;
247
+	option->tag = DHCP_END;
248
+	options->len = 1;
249
+}
250
+
230
 /**
251
 /**
231
  * Allocate space for a block of DHCP options
252
  * Allocate space for a block of DHCP options
232
  *
253
  *
238
  */
259
  */
239
 struct dhcp_option_block * alloc_dhcp_options ( size_t max_len ) {
260
 struct dhcp_option_block * alloc_dhcp_options ( size_t max_len ) {
240
 	struct dhcp_option_block *options;
261
 	struct dhcp_option_block *options;
241
-	struct dhcp_option *option;
242
 
262
 
243
 	options = malloc ( sizeof ( *options ) + max_len );
263
 	options = malloc ( sizeof ( *options ) + max_len );
244
 	if ( options ) {
264
 	if ( options ) {
245
-		options->data = ( ( void * ) options + sizeof ( *options ) );
246
-		options->max_len = max_len;
247
-		if ( max_len ) {
248
-			option = options->data;
249
-			option->tag = DHCP_END;
250
-			options->len = 1;
251
-		}
265
+		init_dhcp_options ( options, 
266
+				    ( (void *) options + sizeof ( *options ) ),
267
+				    max_len );
252
 	}
268
 	}
253
 	return options;
269
 	return options;
254
 }
270
 }

+ 196
- 0
src/net/udp/dhcp.c View File

17
  */
17
  */
18
 
18
 
19
 #include <string.h>
19
 #include <string.h>
20
+#include <errno.h>
20
 #include <assert.h>
21
 #include <assert.h>
21
 #include <byteswap.h>
22
 #include <byteswap.h>
22
 #include <gpxe/netdevice.h>
23
 #include <gpxe/netdevice.h>
28
  *
29
  *
29
  */
30
  */
30
 
31
 
32
+struct dhcp_session {
33
+	struct net_device *netdev;
34
+	uint32_t xid;
35
+};
36
+
37
+/** DHCP operation types
38
+ *
39
+ * This table maps from DHCP message types (i.e. values of the @c
40
+ * DHCP_MESSAGE_TYPE option) to values of the "op" field within a DHCP
41
+ * packet.
42
+ */
43
+static const uint8_t dhcp_op[] = {
44
+	[DHCPDISCOVER]	= BOOTP_REQUEST,
45
+	[DHCPOFFER]	= BOOTP_REPLY,
46
+	[DHCPREQUEST]	= BOOTP_REQUEST,
47
+	[DHCPDECLINE]	= BOOTP_REQUEST,
48
+	[DHCPACK]	= BOOTP_REPLY,
49
+	[DHCPNAK]	= BOOTP_REPLY,
50
+	[DHCPRELEASE]	= BOOTP_REQUEST,
51
+	[DHCPINFORM]	= BOOTP_REQUEST,
52
+};
53
+
54
+/** DHCP packet option block fill order
55
+ *
56
+ * This is the order in which option blocks are filled when
57
+ * reassembling a DHCP packet.  We fill the smallest field ("sname")
58
+ * first, to maximise the chances of being able to fit large options
59
+ * within fields which are large enough to contain them.
60
+ */
61
+enum dhcp_packet_option_block_fill_order {
62
+	OPTS_SNAME = 0,
63
+	OPTS_FILE,
64
+	OPTS_MAIN,
65
+	NUM_OPT_BLOCKS
66
+};
67
+
68
+/** DHCP option blocks within a DHCP packet
69
+ *
70
+ * A DHCP packet contains three fields which can be used to contain
71
+ * options: the actual "options" field plus the "file" and "sname"
72
+ * fields (which can be overloaded to contain options).
73
+ */
74
+struct dhcp_packet_option_blocks {
75
+	struct dhcp_option_block options[NUM_OPT_BLOCKS];
76
+};
77
+
78
+/**
79
+ * Set option within DHCP packet
80
+ *
81
+ * @v optblocks		DHCP packet option blocks
82
+ * @v tag		DHCP option tag
83
+ * @v data		New value for DHCP option
84
+ * @v len		Length of value, in bytes
85
+ * @ret option		DHCP option, or NULL
86
+ *
87
+ * Sets the option within the first available options block within the
88
+ * DHCP packet.  Option blocks are tried in the order specified by @c
89
+ * dhcp_option_block_fill_order.
90
+ */
91
+static struct dhcp_option *
92
+set_dhcp_packet_option ( struct dhcp_packet_option_blocks *optblocks,
93
+			 unsigned int tag, const void *data, size_t len ) {
94
+	struct dhcp_option_block *options;
95
+	struct dhcp_option *option;
96
+
97
+	for ( options = optblocks->options ;
98
+	      options < &optblocks->options[NUM_OPT_BLOCKS] ; options++ ) {
99
+		option = set_dhcp_option ( options, tag, data, len );
100
+		if ( option )
101
+			return option;
102
+	}
103
+	return NULL;
104
+}
105
+
106
+/**
107
+ * Copy options to DHCP packet
108
+ *
109
+ * @v optblocks		DHCP packet option blocks
110
+ * @v encapsulator	Encapsulating option, or zero
111
+ * @ret rc		Return status code
112
+ * 
113
+ * Copies options from DHCP options blocks into a DHCP packet.  Most
114
+ * options are copied verbatim.  Recognised encapsulated options
115
+ * fields are handled as such.  Selected options (e.g. @c
116
+ * DHCP_OPTION_OVERLOAD) are always ignored, since these special cases
117
+ * are handled by other code.
118
+ */
119
+static int
120
+copy_dhcp_options_to_packet ( struct dhcp_packet_option_blocks *optblocks,
121
+			      unsigned int encapsulator ) {
122
+	unsigned int subtag;
123
+	unsigned int tag;
124
+	struct dhcp_option *option;
125
+	struct dhcp_option *copied;
126
+	int rc;
127
+
128
+	for ( subtag = DHCP_MIN_OPTION; subtag <= DHCP_MAX_OPTION; subtag++ ) {
129
+		tag = DHCP_ENCAP_OPT ( encapsulator, subtag );
130
+		switch ( tag ) {
131
+		case DHCP_OPTION_OVERLOAD:
132
+			/* Hard-coded in packets we reassemble; skip
133
+			 * this option
134
+			 */
135
+			break;
136
+		case DHCP_EB_ENCAP:
137
+		case DHCP_VENDOR_ENCAP:
138
+			/* Process encapsulated options field */
139
+			if ( ( rc = copy_dhcp_options_to_packet ( optblocks,
140
+								  tag ) ) != 0)
141
+				return rc;
142
+			break;
143
+		default:
144
+			/* Copy option to reassembled packet */
145
+			option = find_global_dhcp_option ( tag );
146
+			if ( ! option )
147
+				break;
148
+			copied = set_dhcp_packet_option ( optblocks, tag,
149
+							  &option->data,
150
+							  option->len );
151
+			if ( ! copied )
152
+				return -ENOSPC;
153
+			break;
154
+		};
155
+	}
156
+
157
+	return 0;
158
+}
159
+
160
+/**
161
+ * Assemble a DHCP packet
162
+ *
163
+ * @v dhcp		DHCP session
164
+ * @v data		Packet to be filled in
165
+ * @v max_len		Length of packet buffer
166
+ * @ret len		Length of assembled packet
167
+ *
168
+ * Reconstruct a DHCP packet from a DHCP options list.
169
+ */
170
+size_t dhcp_assemble ( struct dhcp_session *dhcp, void *data,
171
+		       size_t max_len ) {
172
+	struct dhcp_packet *dhcppkt = data;
173
+	struct dhcp_option *option;
174
+	struct dhcp_packet_option_blocks optblocks;
175
+	unsigned int dhcp_message_type;
176
+	static const uint8_t overloading = ( DHCP_OPTION_OVERLOAD_FILE |
177
+					     DHCP_OPTION_OVERLOAD_SNAME );
178
+
179
+	/* Fill in constant fields */
180
+	memset ( dhcppkt, 0, max_len );
181
+	dhcppkt->xid = dhcp->xid;
182
+	dhcppkt->magic = htonl ( DHCP_MAGIC_COOKIE );
183
+
184
+	/* Derive "op" field from DHCP_MESSAGE_TYPE option value */
185
+	dhcp_message_type = find_global_dhcp_num_option ( DHCP_MESSAGE_TYPE );
186
+	dhcppkt->op = dhcp_op[dhcp_message_type];
187
+
188
+	/* Fill in NIC details */
189
+	dhcppkt->htype = dhcp->netdev->ll_protocol->ll_proto;
190
+	dhcppkt->hlen = dhcp->netdev->ll_protocol->ll_addr_len;
191
+	memcpy ( dhcppkt->chaddr, dhcp->netdev->ll_addr, dhcppkt->hlen );
192
+
193
+	/* Fill in IP addresses if present */
194
+	option = find_global_dhcp_option ( DHCP_EB_YIADDR );
195
+	if ( option ) {
196
+		memcpy ( &dhcppkt->yiaddr, &option->data,
197
+			 sizeof ( dhcppkt->yiaddr ) );
198
+	}
199
+	option = find_global_dhcp_option ( DHCP_EB_SIADDR );
200
+	if ( option ) {
201
+		memcpy ( &dhcppkt->siaddr, &option->data,
202
+			 sizeof ( dhcppkt->siaddr ) );
203
+	}
204
+
205
+	/* Initialise option blocks */
206
+	init_dhcp_options ( &optblocks.options[OPTS_MAIN], dhcppkt->options,
207
+			    ( max_len -
208
+			      offsetof ( typeof ( *dhcppkt ), options ) ) );
209
+	init_dhcp_options ( &optblocks.options[OPTS_FILE], dhcppkt->file,
210
+			    sizeof ( dhcppkt->file ) );
211
+	init_dhcp_options ( &optblocks.options[OPTS_SNAME], dhcppkt->sname,
212
+			    sizeof ( dhcppkt->sname ) );
213
+	set_dhcp_option ( &optblocks.options[OPTS_MAIN], DHCP_OPTION_OVERLOAD,
214
+			  &overloading, sizeof ( overloading ) );
215
+
216
+	/* Populate option blocks */
217
+	copy_dhcp_options_to_packet ( &optblocks, 0 );
218
+
219
+	return ( offsetof ( typeof ( *dhcppkt ), options )
220
+		 + optblocks.options[OPTS_MAIN].len );
221
+}
222
+
31
 /**
223
 /**
32
  * Calculate used length of a field containing DHCP options
224
  * Calculate used length of a field containing DHCP options
33
  *
225
  *
107
 	size_t options_len;
299
 	size_t options_len;
108
 	unsigned int overloading;
300
 	unsigned int overloading;
109
 
301
 
302
+	/* Sanity check */
303
+	if ( len < sizeof ( *dhcppkt ) )
304
+		return NULL;
305
+
110
 	/* Calculate size of resulting concatenated option block:
306
 	/* Calculate size of resulting concatenated option block:
111
 	 *
307
 	 *
112
 	 *   The "options" field : length of the field minus the DHCP_END tag.
308
 	 *   The "options" field : length of the field minus the DHCP_END tag.

Loading…
Cancel
Save