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

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

@@ -118,6 +118,12 @@ struct dhcp_packet {
118 118
  */
119 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 127
 /** Option overloading
122 128
  *
123 129
  * The value of this option is the bitwise-OR of zero or more
@@ -131,6 +137,17 @@ struct dhcp_packet {
131 137
 /** The "sname" field is overloaded to contain extra DHCP options */
132 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 151
 /** TFTP server name
135 152
  *
136 153
  * This option replaces the fixed "sname" field, when that field is
@@ -178,6 +195,9 @@ struct dhcp_packet {
178 195
  */
179 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 201
 /** End of options
182 202
  *
183 203
  * This tag does not have a length field; it is always only a single
@@ -260,6 +280,8 @@ find_dhcp_option ( struct dhcp_option_block *options, unsigned int tag );
260 280
 extern struct dhcp_option * find_global_dhcp_option ( unsigned int tag );
261 281
 extern void register_dhcp_options ( struct dhcp_option_block *options );
262 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 285
 extern struct dhcp_option_block * alloc_dhcp_options ( size_t max_len );
264 286
 extern void free_dhcp_options ( struct dhcp_option_block *options );
265 287
 extern struct dhcp_option *

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

@@ -227,6 +227,27 @@ void unregister_dhcp_options ( struct dhcp_option_block *options ) {
227 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 252
  * Allocate space for a block of DHCP options
232 253
  *
@@ -238,17 +259,12 @@ void unregister_dhcp_options ( struct dhcp_option_block *options ) {
238 259
  */
239 260
 struct dhcp_option_block * alloc_dhcp_options ( size_t max_len ) {
240 261
 	struct dhcp_option_block *options;
241
-	struct dhcp_option *option;
242 262
 
243 263
 	options = malloc ( sizeof ( *options ) + max_len );
244 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 269
 	return options;
254 270
 }

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

@@ -17,6 +17,7 @@
17 17
  */
18 18
 
19 19
 #include <string.h>
20
+#include <errno.h>
20 21
 #include <assert.h>
21 22
 #include <byteswap.h>
22 23
 #include <gpxe/netdevice.h>
@@ -28,6 +29,197 @@
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 224
  * Calculate used length of a field containing DHCP options
33 225
  *
@@ -107,6 +299,10 @@ struct dhcp_option_block * dhcp_parse ( const void *data, size_t len ) {
107 299
 	size_t options_len;
108 300
 	unsigned int overloading;
109 301
 
302
+	/* Sanity check */
303
+	if ( len < sizeof ( *dhcppkt ) )
304
+		return NULL;
305
+
110 306
 	/* Calculate size of resulting concatenated option block:
111 307
 	 *
112 308
 	 *   The "options" field : length of the field minus the DHCP_END tag.

Loading…
Cancel
Save