Add sketch code to reassemble a DHCP packet from our internal "everything
[people/mcb30/gpxe.git] / src / net / udp / dhcp.c
1 /*
2  * Copyright (C) 2006 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 <string.h>
20 #include <errno.h>
21 #include <assert.h>
22 #include <byteswap.h>
23 #include <gpxe/netdevice.h>
24 #include <gpxe/dhcp.h>
25
26 /** @file
27  *
28  * Dynamic Host Configuration Protocol
29  *
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
223 /**
224  * Calculate used length of a field containing DHCP options
225  *
226  * @v data              Field containing DHCP options
227  * @v max_len           Field length
228  * @ret len             Used length (excluding the @c DHCP_END tag)
229  */
230 static size_t dhcp_field_len ( const void *data, size_t max_len ) {
231         struct dhcp_option_block options;
232         struct dhcp_option *end;
233
234         options.data = ( ( void * ) data );
235         options.len = max_len;
236         end = find_dhcp_option ( &options, DHCP_END );
237         return ( end ? ( ( ( void * ) end ) - data ) : 0 );
238 }
239
240 /**
241  * Merge field containing DHCP options or string into DHCP options block
242  *
243  * @v options           DHCP option block
244  * @v data              Field containing DHCP options
245  * @v max_len           Field length
246  * @v tag               DHCP option tag, or 0
247  *
248  * If @c tag is non-zero, the field will be treated as a
249  * NUL-terminated string representing the value of the specified DHCP
250  * option.  If @c tag is zero, the field will be treated as a block of
251  * DHCP options, and simply appended to the existing options in the
252  * option block.
253  *
254  * The caller must ensure that there is enough space in the options
255  * block to perform the merge.
256  */
257 static void merge_dhcp_field ( struct dhcp_option_block *options,
258                                const void *data, size_t max_len,
259                                unsigned int tag ) {
260         size_t len;
261         void *dest;
262         struct dhcp_option *end;
263
264         if ( tag ) {
265                 set_dhcp_option ( options, tag, data, strlen ( data ) );
266         } else {
267                 len = dhcp_field_len ( data, max_len );
268                 dest = ( options->data + options->len - 1 );
269                 memcpy ( dest, data, len );
270                 options->len += len;
271                 end = ( dest + len );
272                 end->tag = DHCP_END;
273         }
274 }
275
276 /**
277  * Parse DHCP packet and construct DHCP options block
278  *
279  * @v data              DHCP packet
280  * @v len               Length of DHCP packet
281  * @ret options         DHCP options block, or NULL
282  *
283  * Parses a received DHCP packet and canonicalises its contents into a
284  * single DHCP options block.  The "file" and "sname" fields are
285  * converted into the corresponding DHCP options (@c
286  * DHCP_BOOTFILE_NAME and @c DHCP_TFTP_SERVER_NAME respectively).  If
287  * these fields are used for option overloading, their options are
288  * merged in to the options block.  The values of the "yiaddr" and
289  * "siaddr" fields will be stored within the options block as the
290  * options @c DHCP_EB_YIADDR and @c DHCP_EB_SIADDR.
291  * 
292  * Note that this call allocates new memory for the constructed DHCP
293  * options block; it is the responsibility of the caller to eventually
294  * free this memory.
295  */
296 struct dhcp_option_block * dhcp_parse ( const void *data, size_t len ) {
297         const struct dhcp_packet *dhcppkt = data;
298         struct dhcp_option_block *options;
299         size_t options_len;
300         unsigned int overloading;
301
302         /* Sanity check */
303         if ( len < sizeof ( *dhcppkt ) )
304                 return NULL;
305
306         /* Calculate size of resulting concatenated option block:
307          *
308          *   The "options" field : length of the field minus the DHCP_END tag.
309          *
310          *   The "file" field : maximum length of the field minus the
311          *   NUL terminator, plus a 2-byte DHCP header or, if used for
312          *   option overloading, the length of the field minus the
313          *   DHCP_END tag.
314          *
315          *   The "sname" field : as for the "file" field.
316          *
317          *   15 bytes for an encapsulated options field to contain the
318          *   value of the "yiaddr" and "siaddr" fields
319          *
320          *   1 byte for a final terminating DHCP_END tag.
321          */
322         options_len = ( ( len - offsetof ( typeof ( *dhcppkt ), options ) ) - 1
323                         + ( sizeof ( dhcppkt->file ) + 1 )
324                         + ( sizeof ( dhcppkt->sname ) + 1 )
325                         + 15 /* yiaddr and siaddr */
326                         + 1 /* DHCP_END tag */ );
327         
328         /* Allocate empty options block of required size */
329         options = alloc_dhcp_options ( options_len );
330         if ( ! options ) {
331                 DBG ( "DHCP could not allocate %d-byte option block\n",
332                       options_len );
333                 return NULL;
334         }
335         
336         /* Merge in "options" field, if this is a DHCP packet */
337         if ( dhcppkt->magic == htonl ( DHCP_MAGIC_COOKIE ) ) {
338                 merge_dhcp_field ( options, dhcppkt->options,
339                                    ( len -
340                                      offsetof ( typeof (*dhcppkt), options ) ),
341                                    0 /* Always contains options */ );
342         }
343
344         /* Identify overloaded fields */
345         overloading = find_dhcp_num_option ( options, DHCP_OPTION_OVERLOAD );
346         
347         /* Merge in "file" and "sname" fields */
348         merge_dhcp_field ( options, dhcppkt->file, sizeof ( dhcppkt->file ),
349                            ( ( overloading & DHCP_OPTION_OVERLOAD_FILE ) ?
350                              DHCP_BOOTFILE_NAME : 0 ) );
351         merge_dhcp_field ( options, dhcppkt->sname, sizeof ( dhcppkt->sname ),
352                            ( ( overloading & DHCP_OPTION_OVERLOAD_SNAME ) ?
353                              DHCP_TFTP_SERVER_NAME : 0 ) );
354
355         /* Set options for "yiaddr" and "siaddr", if present */
356         if ( dhcppkt->yiaddr.s_addr ) {
357                 set_dhcp_option ( options, DHCP_EB_YIADDR,
358                                   &dhcppkt->yiaddr, sizeof (dhcppkt->yiaddr) );
359         }
360         if ( dhcppkt->siaddr.s_addr ) {
361                 set_dhcp_option ( options, DHCP_EB_SIADDR,
362                                   &dhcppkt->siaddr, sizeof (dhcppkt->siaddr) );
363         }
364         
365         assert ( options->len <= options->max_len );
366
367         return options;
368 }