|
@@ -46,105 +46,106 @@ static const uint8_t dhcp_op[] = {
|
46
|
46
|
[DHCPINFORM] = BOOTP_REQUEST,
|
47
|
47
|
};
|
48
|
48
|
|
49
|
|
-/** DHCP packet option block fill order
|
50
|
|
- *
|
51
|
|
- * This is the order in which option blocks are filled when
|
52
|
|
- * reassembling a DHCP packet. We fill the smallest field ("sname")
|
53
|
|
- * first, to maximise the chances of being able to fit large options
|
54
|
|
- * within fields which are large enough to contain them.
|
55
|
|
- */
|
56
|
|
-enum dhcp_packet_option_block_fill_order {
|
57
|
|
- OPTS_SNAME = 0,
|
58
|
|
- OPTS_FILE,
|
59
|
|
- OPTS_MAIN,
|
60
|
|
- NUM_OPT_BLOCKS
|
61
|
|
-};
|
62
|
|
-
|
63
|
|
-/** DHCP option blocks within a DHCP packet
|
64
|
|
- *
|
65
|
|
- * A DHCP packet contains three fields which can be used to contain
|
66
|
|
- * options: the actual "options" field plus the "file" and "sname"
|
67
|
|
- * fields (which can be overloaded to contain options).
|
68
|
|
- */
|
69
|
|
-struct dhcp_packet_option_blocks {
|
70
|
|
- struct dhcp_option_block options[NUM_OPT_BLOCKS];
|
71
|
|
-};
|
72
|
|
-
|
73
|
49
|
/**
|
74
|
50
|
* Set option within DHCP packet
|
75
|
51
|
*
|
76
|
|
- * @v optblocks DHCP packet option blocks
|
|
52
|
+ * @v dhcppkt DHCP packet
|
77
|
53
|
* @v tag DHCP option tag
|
78
|
54
|
* @v data New value for DHCP option
|
79
|
55
|
* @v len Length of value, in bytes
|
80
|
|
- * @ret option DHCP option, or NULL
|
|
56
|
+ * @ret rc Return status code
|
81
|
57
|
*
|
82
|
58
|
* Sets the option within the first available options block within the
|
83
|
59
|
* DHCP packet. Option blocks are tried in the order specified by @c
|
84
|
60
|
* dhcp_option_block_fill_order.
|
|
61
|
+ *
|
|
62
|
+ * The magic options @c DHCP_EB_YIADDR and @c DHCP_EB_SIADDR are
|
|
63
|
+ * intercepted and inserted into the appropriate fixed fields within
|
|
64
|
+ * the DHCP packet. The option @c DHCP_OPTION_OVERLOAD is silently
|
|
65
|
+ * ignored, since our DHCP packet assembly method relies on always
|
|
66
|
+ * having option overloading in use.
|
85
|
67
|
*/
|
86
|
|
-static struct dhcp_option *
|
87
|
|
-set_dhcp_packet_option ( struct dhcp_packet_option_blocks *optblocks,
|
88
|
|
- unsigned int tag, const void *data, size_t len ) {
|
|
68
|
+static int set_dhcp_packet_option ( struct dhcp_packet *dhcppkt,
|
|
69
|
+ unsigned int tag, const void *data,
|
|
70
|
+ size_t len ) {
|
|
71
|
+ struct dhcphdr *dhcphdr = dhcppkt->dhcphdr;
|
89
|
72
|
struct dhcp_option_block *options;
|
90
|
|
- struct dhcp_option *option;
|
|
73
|
+ struct dhcp_option *option = NULL;
|
91
|
74
|
|
92
|
|
- for ( options = optblocks->options ;
|
93
|
|
- options < &optblocks->options[NUM_OPT_BLOCKS] ; options++ ) {
|
|
75
|
+ /* Special-case the magic options */
|
|
76
|
+ switch ( tag ) {
|
|
77
|
+ case DHCP_OPTION_OVERLOAD:
|
|
78
|
+ /* Hard-coded in packets we create; always ignore */
|
|
79
|
+ return 0;
|
|
80
|
+ case DHCP_EB_YIADDR:
|
|
81
|
+ memcpy ( &dhcphdr->yiaddr, data, sizeof ( dhcphdr->yiaddr ) );
|
|
82
|
+ return 0;
|
|
83
|
+ case DHCP_EB_SIADDR:
|
|
84
|
+ memcpy ( &dhcphdr->siaddr, data, sizeof ( dhcphdr->siaddr ) );
|
|
85
|
+ return 0;
|
|
86
|
+ default:
|
|
87
|
+ /* Continue processing as normal */
|
|
88
|
+ break;
|
|
89
|
+ }
|
|
90
|
+
|
|
91
|
+ /* Set option in first available options block */
|
|
92
|
+ for ( options = dhcppkt->options ;
|
|
93
|
+ options < &dhcppkt->options[NUM_OPT_BLOCKS] ; options++ ) {
|
94
|
94
|
option = set_dhcp_option ( options, tag, data, len );
|
95
|
95
|
if ( option )
|
96
|
|
- return option;
|
|
96
|
+ break;
|
97
|
97
|
}
|
98
|
|
- return NULL;
|
|
98
|
+
|
|
99
|
+ /* Update DHCP packet length */
|
|
100
|
+ dhcppkt->len = ( offsetof ( typeof ( *dhcppkt->dhcphdr ), options )
|
|
101
|
+ + dhcppkt->options[OPTS_MAIN].len );
|
|
102
|
+
|
|
103
|
+ return ( option ? 0 : -ENOSPC );
|
99
|
104
|
}
|
100
|
105
|
|
101
|
106
|
/**
|
102
|
|
- * Copy options to DHCP packet
|
|
107
|
+ * Set options within DHCP packet
|
103
|
108
|
*
|
104
|
|
- * @v optblocks DHCP packet option blocks
|
|
109
|
+ * @v dhcppkt DHCP packet
|
|
110
|
+ * @v options DHCP option block, or NULL
|
105
|
111
|
* @v encapsulator Encapsulating option, or zero
|
106
|
112
|
* @ret rc Return status code
|
107
|
113
|
*
|
108
|
|
- * Copies options from DHCP options blocks into a DHCP packet. Most
|
109
|
|
- * options are copied verbatim. Recognised encapsulated options
|
110
|
|
- * fields are handled as such. Selected options (e.g. @c
|
111
|
|
- * DHCP_OPTION_OVERLOAD) are always ignored, since these special cases
|
112
|
|
- * are handled by other code.
|
|
114
|
+ * Copies options with the specified encapsulator from DHCP options
|
|
115
|
+ * blocks into a DHCP packet. Most options are copied verbatim.
|
|
116
|
+ * Recognised encapsulated options fields are handled as such.
|
|
117
|
+ *
|
|
118
|
+ * @c options may specify a single options block, or be left as NULL
|
|
119
|
+ * in order to copy options from all registered options blocks.
|
113
|
120
|
*/
|
114
|
|
-static int
|
115
|
|
-copy_dhcp_options_to_packet ( struct dhcp_packet_option_blocks *optblocks,
|
116
|
|
- unsigned int encapsulator ) {
|
|
121
|
+static int set_dhcp_packet_encap_options ( struct dhcp_packet *dhcppkt,
|
|
122
|
+ struct dhcp_option_block *options,
|
|
123
|
+ unsigned int encapsulator ) {
|
117
|
124
|
unsigned int subtag;
|
118
|
125
|
unsigned int tag;
|
119
|
126
|
struct dhcp_option *option;
|
120
|
|
- struct dhcp_option *copied;
|
121
|
127
|
int rc;
|
122
|
128
|
|
123
|
129
|
for ( subtag = DHCP_MIN_OPTION; subtag <= DHCP_MAX_OPTION; subtag++ ) {
|
124
|
130
|
tag = DHCP_ENCAP_OPT ( encapsulator, subtag );
|
125
|
131
|
switch ( tag ) {
|
126
|
|
- case DHCP_OPTION_OVERLOAD:
|
127
|
|
- /* Hard-coded in packets we reassemble; skip
|
128
|
|
- * this option
|
129
|
|
- */
|
130
|
|
- break;
|
131
|
132
|
case DHCP_EB_ENCAP:
|
132
|
133
|
case DHCP_VENDOR_ENCAP:
|
133
|
134
|
/* Process encapsulated options field */
|
134
|
|
- if ( ( rc = copy_dhcp_options_to_packet ( optblocks,
|
135
|
|
- tag ) ) != 0)
|
|
135
|
+ if ( ( rc = set_dhcp_packet_encap_options ( dhcppkt,
|
|
136
|
+ options,
|
|
137
|
+ tag )) !=0)
|
136
|
138
|
return rc;
|
137
|
139
|
break;
|
138
|
140
|
default:
|
139
|
141
|
/* Copy option to reassembled packet */
|
140
|
|
- option = find_global_dhcp_option ( tag );
|
|
142
|
+ option = find_dhcp_option ( options, tag );
|
141
|
143
|
if ( ! option )
|
142
|
144
|
break;
|
143
|
|
- copied = set_dhcp_packet_option ( optblocks, tag,
|
144
|
|
- &option->data,
|
145
|
|
- option->len );
|
146
|
|
- if ( ! copied )
|
147
|
|
- return -ENOSPC;
|
|
145
|
+ if ( ( rc = set_dhcp_packet_option ( dhcppkt, tag,
|
|
146
|
+ &option->data,
|
|
147
|
+ option->len)) !=0)
|
|
148
|
+ return rc;
|
148
|
149
|
break;
|
149
|
150
|
};
|
150
|
151
|
}
|
|
@@ -153,66 +154,77 @@ copy_dhcp_options_to_packet ( struct dhcp_packet_option_blocks *optblocks,
|
153
|
154
|
}
|
154
|
155
|
|
155
|
156
|
/**
|
156
|
|
- * Assemble a DHCP packet
|
|
157
|
+ * Set options within DHCP packet
|
|
158
|
+ *
|
|
159
|
+ * @v dhcppkt DHCP packet
|
|
160
|
+ * @v options DHCP option block, or NULL
|
|
161
|
+ * @ret rc Return status code
|
|
162
|
+ *
|
|
163
|
+ * Copies options from DHCP options blocks into a DHCP packet. Most
|
|
164
|
+ * options are copied verbatim. Recognised encapsulated options
|
|
165
|
+ * fields are handled as such.
|
|
166
|
+ *
|
|
167
|
+ * @c options may specify a single options block, or be left as NULL
|
|
168
|
+ * in order to copy options from all registered options blocks.
|
|
169
|
+ */
|
|
170
|
+static int set_dhcp_packet_options ( struct dhcp_packet *dhcppkt,
|
|
171
|
+ struct dhcp_option_block *options ) {
|
|
172
|
+ return set_dhcp_packet_encap_options ( dhcppkt, options, 0 );
|
|
173
|
+}
|
|
174
|
+
|
|
175
|
+/**
|
|
176
|
+ * Create a DHCP packet
|
157
|
177
|
*
|
158
|
178
|
* @v dhcp DHCP session
|
159
|
|
- * @v data Packet to be filled in
|
160
|
|
- * @v max_len Length of packet buffer
|
161
|
|
- * @ret len Length of assembled packet
|
|
179
|
+ * @v msgtype DHCP message type
|
|
180
|
+ * @v data Buffer for DHCP packet
|
|
181
|
+ * @v max_len Size of DHCP packet buffer
|
|
182
|
+ * @v dhcppkt DHCP packet structure to fill in
|
|
183
|
+ * @ret rc Return status code
|
162
|
184
|
*
|
163
|
|
- * Reconstruct a DHCP packet from a DHCP options list.
|
|
185
|
+ * Creates a DHCP packet in the specified buffer, and fills out a @c
|
|
186
|
+ * dhcp_packet structure that can be passed to
|
|
187
|
+ * set_dhcp_packet_option() or set_dhcp_packet_options().
|
164
|
188
|
*/
|
165
|
|
-size_t dhcp_assemble ( struct dhcp_session *dhcp, void *data,
|
166
|
|
- size_t max_len ) {
|
167
|
|
- struct dhcp_packet *dhcppkt = data;
|
168
|
|
- struct dhcp_option *option;
|
169
|
|
- struct dhcp_packet_option_blocks optblocks;
|
170
|
|
- unsigned int dhcp_message_type;
|
|
189
|
+int create_dhcp_packet ( struct dhcp_session *dhcp, uint8_t msgtype,
|
|
190
|
+ void *data, size_t max_len,
|
|
191
|
+ struct dhcp_packet *dhcppkt ) {
|
|
192
|
+ struct dhcphdr *dhcphdr = data;
|
171
|
193
|
static const uint8_t overloading = ( DHCP_OPTION_OVERLOAD_FILE |
|
172
|
194
|
DHCP_OPTION_OVERLOAD_SNAME );
|
173
|
195
|
|
174
|
|
- /* Fill in constant fields */
|
175
|
|
- memset ( dhcppkt, 0, max_len );
|
176
|
|
- dhcppkt->xid = dhcp->xid;
|
177
|
|
- dhcppkt->magic = htonl ( DHCP_MAGIC_COOKIE );
|
178
|
|
-
|
179
|
|
- /* Derive "op" field from DHCP_MESSAGE_TYPE option value */
|
180
|
|
- dhcp_message_type = find_global_dhcp_num_option ( DHCP_MESSAGE_TYPE );
|
181
|
|
- dhcppkt->op = dhcp_op[dhcp_message_type];
|
182
|
|
-
|
183
|
|
- /* Fill in NIC details */
|
184
|
|
- dhcppkt->htype = ntohs ( dhcp->netdev->ll_protocol->ll_proto );
|
185
|
|
- dhcppkt->hlen = dhcp->netdev->ll_protocol->ll_addr_len;
|
186
|
|
- memcpy ( dhcppkt->chaddr, dhcp->netdev->ll_addr, dhcppkt->hlen );
|
187
|
|
-
|
188
|
|
- /* Fill in IP addresses if present */
|
189
|
|
- option = find_global_dhcp_option ( DHCP_EB_YIADDR );
|
190
|
|
- if ( option ) {
|
191
|
|
- memcpy ( &dhcppkt->yiaddr, &option->data,
|
192
|
|
- sizeof ( dhcppkt->yiaddr ) );
|
193
|
|
- }
|
194
|
|
- option = find_global_dhcp_option ( DHCP_EB_SIADDR );
|
195
|
|
- if ( option ) {
|
196
|
|
- memcpy ( &dhcppkt->siaddr, &option->data,
|
197
|
|
- sizeof ( dhcppkt->siaddr ) );
|
198
|
|
- }
|
199
|
|
-
|
200
|
|
- /* Initialise option blocks */
|
201
|
|
- init_dhcp_options ( &optblocks.options[OPTS_MAIN], dhcppkt->options,
|
|
196
|
+ /* Initialise DHCP packet structure */
|
|
197
|
+ dhcppkt->dhcphdr = dhcphdr;
|
|
198
|
+ dhcppkt->max_len = max_len;
|
|
199
|
+ init_dhcp_options ( &dhcppkt->options[OPTS_MAIN], dhcphdr->options,
|
202
|
200
|
( max_len -
|
203
|
|
- offsetof ( typeof ( *dhcppkt ), options ) ) );
|
204
|
|
- init_dhcp_options ( &optblocks.options[OPTS_FILE], dhcppkt->file,
|
205
|
|
- sizeof ( dhcppkt->file ) );
|
206
|
|
- init_dhcp_options ( &optblocks.options[OPTS_SNAME], dhcppkt->sname,
|
207
|
|
- sizeof ( dhcppkt->sname ) );
|
208
|
|
- set_dhcp_option ( &optblocks.options[OPTS_MAIN], DHCP_OPTION_OVERLOAD,
|
209
|
|
- &overloading, sizeof ( overloading ) );
|
|
201
|
+ offsetof ( typeof ( *dhcphdr ), options ) ) );
|
|
202
|
+ init_dhcp_options ( &dhcppkt->options[OPTS_FILE], dhcphdr->file,
|
|
203
|
+ sizeof ( dhcphdr->file ) );
|
|
204
|
+ init_dhcp_options ( &dhcppkt->options[OPTS_SNAME], dhcphdr->sname,
|
|
205
|
+ sizeof ( dhcphdr->sname ) );
|
|
206
|
+
|
|
207
|
+ /* Initialise DHCP packet content */
|
|
208
|
+ memset ( dhcphdr, 0, max_len );
|
|
209
|
+ dhcphdr->xid = dhcp->xid;
|
|
210
|
+ dhcphdr->magic = htonl ( DHCP_MAGIC_COOKIE );
|
|
211
|
+ dhcphdr->htype = ntohs ( dhcp->netdev->ll_protocol->ll_proto );
|
|
212
|
+ dhcphdr->hlen = dhcp->netdev->ll_protocol->ll_addr_len;
|
|
213
|
+ memcpy ( dhcphdr->chaddr, dhcp->netdev->ll_addr, dhcphdr->hlen );
|
|
214
|
+ dhcphdr->op = dhcp_op[msgtype];
|
|
215
|
+
|
|
216
|
+ /* Set DHCP_OPTION_OVERLOAD option within the main options block */
|
|
217
|
+ if ( ! set_dhcp_option ( &dhcppkt->options[OPTS_MAIN],
|
|
218
|
+ DHCP_OPTION_OVERLOAD, &overloading,
|
|
219
|
+ sizeof ( overloading ) ) )
|
|
220
|
+ return -ENOSPC;
|
210
|
221
|
|
211
|
|
- /* Populate option blocks */
|
212
|
|
- copy_dhcp_options_to_packet ( &optblocks, 0 );
|
|
222
|
+ /* Set DHCP_MESSAGE_TYPE option */
|
|
223
|
+ if ( ! set_dhcp_packet_option ( dhcppkt, DHCP_MESSAGE_TYPE,
|
|
224
|
+ &msgtype, sizeof ( msgtype ) ) )
|
|
225
|
+ return -ENOSPC;
|
213
|
226
|
|
214
|
|
- return ( offsetof ( typeof ( *dhcppkt ), options )
|
215
|
|
- + optblocks.options[OPTS_MAIN].len );
|
|
227
|
+ return 0;
|
216
|
228
|
}
|
217
|
229
|
|
218
|
230
|
/**
|
|
@@ -280,22 +292,24 @@ static void merge_dhcp_field ( struct dhcp_option_block *options,
|
280
|
292
|
* converted into the corresponding DHCP options (@c
|
281
|
293
|
* DHCP_BOOTFILE_NAME and @c DHCP_TFTP_SERVER_NAME respectively). If
|
282
|
294
|
* these fields are used for option overloading, their options are
|
283
|
|
- * merged in to the options block. The values of the "yiaddr" and
|
284
|
|
- * "siaddr" fields will be stored within the options block as the
|
285
|
|
- * options @c DHCP_EB_YIADDR and @c DHCP_EB_SIADDR.
|
|
295
|
+ * merged in to the options block.
|
|
296
|
+ *
|
|
297
|
+ * The values of the "yiaddr" and "siaddr" fields will be stored
|
|
298
|
+ * within the options block as the magic options @c DHCP_EB_YIADDR and
|
|
299
|
+ * @c DHCP_EB_SIADDR.
|
286
|
300
|
*
|
287
|
301
|
* Note that this call allocates new memory for the constructed DHCP
|
288
|
302
|
* options block; it is the responsibility of the caller to eventually
|
289
|
303
|
* free this memory.
|
290
|
304
|
*/
|
291
|
305
|
struct dhcp_option_block * dhcp_parse ( const void *data, size_t len ) {
|
292
|
|
- const struct dhcp_packet *dhcppkt = data;
|
|
306
|
+ const struct dhcphdr *dhcphdr = data;
|
293
|
307
|
struct dhcp_option_block *options;
|
294
|
308
|
size_t options_len;
|
295
|
309
|
unsigned int overloading;
|
296
|
310
|
|
297
|
311
|
/* Sanity check */
|
298
|
|
- if ( len < sizeof ( *dhcppkt ) )
|
|
312
|
+ if ( len < sizeof ( *dhcphdr ) )
|
299
|
313
|
return NULL;
|
300
|
314
|
|
301
|
315
|
/* Calculate size of resulting concatenated option block:
|
|
@@ -314,9 +328,9 @@ struct dhcp_option_block * dhcp_parse ( const void *data, size_t len ) {
|
314
|
328
|
*
|
315
|
329
|
* 1 byte for a final terminating DHCP_END tag.
|
316
|
330
|
*/
|
317
|
|
- options_len = ( ( len - offsetof ( typeof ( *dhcppkt ), options ) ) - 1
|
318
|
|
- + ( sizeof ( dhcppkt->file ) + 1 )
|
319
|
|
- + ( sizeof ( dhcppkt->sname ) + 1 )
|
|
331
|
+ options_len = ( ( len - offsetof ( typeof ( *dhcphdr ), options ) ) - 1
|
|
332
|
+ + ( sizeof ( dhcphdr->file ) + 1 )
|
|
333
|
+ + ( sizeof ( dhcphdr->sname ) + 1 )
|
320
|
334
|
+ 15 /* yiaddr and siaddr */
|
321
|
335
|
+ 1 /* DHCP_END tag */ );
|
322
|
336
|
|
|
@@ -329,10 +343,10 @@ struct dhcp_option_block * dhcp_parse ( const void *data, size_t len ) {
|
329
|
343
|
}
|
330
|
344
|
|
331
|
345
|
/* Merge in "options" field, if this is a DHCP packet */
|
332
|
|
- if ( dhcppkt->magic == htonl ( DHCP_MAGIC_COOKIE ) ) {
|
333
|
|
- merge_dhcp_field ( options, dhcppkt->options,
|
|
346
|
+ if ( dhcphdr->magic == htonl ( DHCP_MAGIC_COOKIE ) ) {
|
|
347
|
+ merge_dhcp_field ( options, dhcphdr->options,
|
334
|
348
|
( len -
|
335
|
|
- offsetof ( typeof (*dhcppkt), options ) ),
|
|
349
|
+ offsetof ( typeof (*dhcphdr), options ) ),
|
336
|
350
|
0 /* Always contains options */ );
|
337
|
351
|
}
|
338
|
352
|
|
|
@@ -340,21 +354,21 @@ struct dhcp_option_block * dhcp_parse ( const void *data, size_t len ) {
|
340
|
354
|
overloading = find_dhcp_num_option ( options, DHCP_OPTION_OVERLOAD );
|
341
|
355
|
|
342
|
356
|
/* Merge in "file" and "sname" fields */
|
343
|
|
- merge_dhcp_field ( options, dhcppkt->file, sizeof ( dhcppkt->file ),
|
|
357
|
+ merge_dhcp_field ( options, dhcphdr->file, sizeof ( dhcphdr->file ),
|
344
|
358
|
( ( overloading & DHCP_OPTION_OVERLOAD_FILE ) ?
|
345
|
359
|
DHCP_BOOTFILE_NAME : 0 ) );
|
346
|
|
- merge_dhcp_field ( options, dhcppkt->sname, sizeof ( dhcppkt->sname ),
|
|
360
|
+ merge_dhcp_field ( options, dhcphdr->sname, sizeof ( dhcphdr->sname ),
|
347
|
361
|
( ( overloading & DHCP_OPTION_OVERLOAD_SNAME ) ?
|
348
|
362
|
DHCP_TFTP_SERVER_NAME : 0 ) );
|
349
|
363
|
|
350
|
|
- /* Set options for "yiaddr" and "siaddr", if present */
|
351
|
|
- if ( dhcppkt->yiaddr.s_addr ) {
|
|
364
|
+ /* Set magic options for "yiaddr" and "siaddr", if present */
|
|
365
|
+ if ( dhcphdr->yiaddr.s_addr ) {
|
352
|
366
|
set_dhcp_option ( options, DHCP_EB_YIADDR,
|
353
|
|
- &dhcppkt->yiaddr, sizeof (dhcppkt->yiaddr) );
|
|
367
|
+ &dhcphdr->yiaddr, sizeof (dhcphdr->yiaddr) );
|
354
|
368
|
}
|
355
|
|
- if ( dhcppkt->siaddr.s_addr ) {
|
|
369
|
+ if ( dhcphdr->siaddr.s_addr ) {
|
356
|
370
|
set_dhcp_option ( options, DHCP_EB_SIADDR,
|
357
|
|
- &dhcppkt->siaddr, sizeof (dhcppkt->siaddr) );
|
|
371
|
+ &dhcphdr->siaddr, sizeof (dhcphdr->siaddr) );
|
358
|
372
|
}
|
359
|
373
|
|
360
|
374
|
assert ( options->len <= options->max_len );
|