These changes are the raw update to qemu-2.6.
[kvmfornfv.git] / qemu / roms / ipxe / src / net / dhcppkt.c
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., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301, USA.
18  *
19  * You can also choose to distribute this program under the terms of
20  * the Unmodified Binary Distribution Licence (as given in the file
21  * COPYING.UBDL), provided that you have satisfied its requirements.
22  */
23
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25
26 #include <stdint.h>
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <errno.h>
30 #include <string.h>
31 #include <ipxe/netdevice.h>
32 #include <ipxe/dhcp.h>
33 #include <ipxe/dhcpopts.h>
34 #include <ipxe/dhcppkt.h>
35
36 /** @file
37  *
38  * DHCP packets
39  *
40  */
41
42 /****************************************************************************
43  *
44  * DHCP packet raw interface
45  *
46  */
47
48 /**
49  * Calculate used length of an IPv4 field within a DHCP packet
50  *
51  * @v data              Field data
52  * @v len               Length of field
53  * @ret used            Used length of field
54  */
55 static size_t used_len_ipv4 ( const void *data, size_t len __unused ) {
56         const struct in_addr *in = data;
57
58         return ( in->s_addr ? sizeof ( *in ) : 0 );
59 }
60
61 /**
62  * Calculate used length of a string field within a DHCP packet
63  *
64  * @v data              Field data
65  * @v len               Length of field
66  * @ret used            Used length of field
67  */
68 static size_t used_len_string ( const void *data, size_t len ) {
69         return strnlen ( data, len );
70 }
71
72 /** A dedicated field within a DHCP packet */
73 struct dhcp_packet_field {
74         /** Settings tag number */
75         unsigned int tag;
76         /** Offset within DHCP packet */
77         uint16_t offset;
78         /** Length of field */
79         uint16_t len;
80         /** Calculate used length of field
81          *
82          * @v data      Field data
83          * @v len       Length of field
84          * @ret used    Used length of field
85          */
86         size_t ( * used_len ) ( const void *data, size_t len );
87 };
88
89 /** Declare a dedicated field within a DHCP packet
90  *
91  * @v _tag              Settings tag number
92  * @v _field            Field name
93  * @v _used_len         Function to calculate used length of field
94  */
95 #define DHCP_PACKET_FIELD( _tag, _field, _used_len ) {                  \
96                 .tag = (_tag),                                          \
97                 .offset = offsetof ( struct dhcphdr, _field ),          \
98                 .len = sizeof ( ( ( struct dhcphdr * ) 0 )->_field ),   \
99                 .used_len = _used_len,                                  \
100         }
101
102 /** Dedicated fields within a DHCP packet */
103 static struct dhcp_packet_field dhcp_packet_fields[] = {
104         DHCP_PACKET_FIELD ( DHCP_EB_YIADDR, yiaddr, used_len_ipv4 ),
105         DHCP_PACKET_FIELD ( DHCP_EB_SIADDR, siaddr, used_len_ipv4 ),
106         DHCP_PACKET_FIELD ( DHCP_TFTP_SERVER_NAME, sname, used_len_string ),
107         DHCP_PACKET_FIELD ( DHCP_BOOTFILE_NAME, file, used_len_string ),
108 };
109
110 /**
111  * Get address of a DHCP packet field
112  *
113  * @v dhcphdr           DHCP packet header
114  * @v field             DHCP packet field
115  * @ret data            Packet field data
116  */
117 static inline void * dhcp_packet_field ( struct dhcphdr *dhcphdr,
118                                          struct dhcp_packet_field *field ) {
119         return ( ( ( void * ) dhcphdr ) + field->offset );
120 }
121
122 /**
123  * Find DHCP packet field corresponding to settings tag number
124  *
125  * @v tag               Settings tag number
126  * @ret field           DHCP packet field, or NULL
127  */
128 static struct dhcp_packet_field *
129 find_dhcp_packet_field ( unsigned int tag ) {
130         struct dhcp_packet_field *field;
131         unsigned int i;
132
133         for ( i = 0 ; i < ( sizeof ( dhcp_packet_fields ) /
134                             sizeof ( dhcp_packet_fields[0] ) ) ; i++ ) {
135                 field = &dhcp_packet_fields[i];
136                 if ( field->tag == tag )
137                         return field;
138         }
139         return NULL;
140 }
141
142 /**
143  * Check applicability of DHCP setting
144  *
145  * @v dhcppkt           DHCP packet
146  * @v tag               Setting tag number
147  * @ret applies         Setting applies within this settings block
148  */
149 static int dhcppkt_applies ( struct dhcp_packet *dhcppkt __unused,
150                              unsigned int tag ) {
151
152         return dhcpopt_applies ( tag );
153 }
154
155 /**
156  * Store value of DHCP packet setting
157  *
158  * @v dhcppkt           DHCP packet
159  * @v tag               Setting tag number
160  * @v data              Setting data, or NULL to clear setting
161  * @v len               Length of setting data
162  * @ret rc              Return status code
163  */
164 int dhcppkt_store ( struct dhcp_packet *dhcppkt, unsigned int tag,
165                     const void *data, size_t len ) {
166         struct dhcp_packet_field *field;
167         void *field_data;
168
169         /* If this is a special field, fill it in */
170         if ( ( field = find_dhcp_packet_field ( tag ) ) != NULL ) {
171                 if ( len > field->len )
172                         return -ENOSPC;
173                 field_data = dhcp_packet_field ( dhcppkt->dhcphdr, field );
174                 memset ( field_data, 0, field->len );
175                 memcpy ( dhcp_packet_field ( dhcppkt->dhcphdr, field ),
176                          data, len );
177                 /* Erase any equivalent option from the options block */
178                 dhcpopt_store ( &dhcppkt->options, tag, NULL, 0 );
179                 return 0;
180         }
181
182         /* Otherwise, use the generic options block */
183         return dhcpopt_store ( &dhcppkt->options, tag, data, len );
184 }
185
186 /**
187  * Fetch value of DHCP packet setting
188  *
189  * @v dhcppkt           DHCP packet
190  * @v tag               Setting tag number
191  * @v data              Buffer to fill with setting data
192  * @v len               Length of buffer
193  * @ret len             Length of setting data, or negative error
194  */
195 int dhcppkt_fetch ( struct dhcp_packet *dhcppkt, unsigned int tag,
196                     void *data, size_t len ) {
197         struct dhcp_packet_field *field;
198         void *field_data;
199         size_t field_len = 0;
200         
201         /* Identify special field, if any */
202         if ( ( field = find_dhcp_packet_field ( tag ) ) != NULL ) {
203                 field_data = dhcp_packet_field ( dhcppkt->dhcphdr, field );
204                 field_len = field->used_len ( field_data, field->len );
205         }
206
207         /* Return special field, if it exists and is populated */
208         if ( field_len ) {
209                 if ( len > field_len )
210                         len = field_len;
211                 memcpy ( data, field_data, len );
212                 return field_len;
213         }
214
215         /* Otherwise, use the generic options block */
216         return dhcpopt_fetch ( &dhcppkt->options, tag, data, len );
217 }
218
219 /****************************************************************************
220  *
221  * DHCP packet settings interface
222  *
223  */
224
225 /**
226  * Check applicability of DHCP setting
227  *
228  * @v settings          Settings block
229  * @v setting           Setting
230  * @ret applies         Setting applies within this settings block
231  */
232 static int dhcppkt_settings_applies ( struct settings *settings,
233                                       const struct setting *setting ) {
234         struct dhcp_packet *dhcppkt =
235                 container_of ( settings, struct dhcp_packet, settings );
236
237         return ( ( setting->scope == NULL ) &&
238                  dhcppkt_applies ( dhcppkt, setting->tag ) );
239 }
240
241 /**
242  * Store value of DHCP setting
243  *
244  * @v settings          Settings block
245  * @v setting           Setting to store
246  * @v data              Setting data, or NULL to clear setting
247  * @v len               Length of setting data
248  * @ret rc              Return status code
249  */
250 static int dhcppkt_settings_store ( struct settings *settings,
251                                     const struct setting *setting,
252                                     const void *data, size_t len ) {
253         struct dhcp_packet *dhcppkt =
254                 container_of ( settings, struct dhcp_packet, settings );
255
256         return dhcppkt_store ( dhcppkt, setting->tag, data, len );
257 }
258
259 /**
260  * Fetch value of DHCP setting
261  *
262  * @v settings          Settings block, or NULL to search all blocks
263  * @v setting           Setting to fetch
264  * @v data              Buffer to fill with setting data
265  * @v len               Length of buffer
266  * @ret len             Length of setting data, or negative error
267  */
268 static int dhcppkt_settings_fetch ( struct settings *settings,
269                                     struct setting *setting,
270                                     void *data, size_t len ) {
271         struct dhcp_packet *dhcppkt =
272                 container_of ( settings, struct dhcp_packet, settings );
273
274         return dhcppkt_fetch ( dhcppkt, setting->tag, data, len );
275 }
276
277 /** DHCP settings operations */
278 static struct settings_operations dhcppkt_settings_operations = {
279         .applies = dhcppkt_settings_applies,
280         .store = dhcppkt_settings_store,
281         .fetch = dhcppkt_settings_fetch,
282 };
283
284 /****************************************************************************
285  *
286  * Constructor
287  *
288  */
289
290 /**
291  * Initialise DHCP packet
292  *
293  * @v dhcppkt           DHCP packet structure to fill in
294  * @v data              DHCP packet raw data
295  * @v max_len           Length of raw data buffer
296  *
297  * Initialise a DHCP packet structure from a data buffer containing a
298  * DHCP packet.
299  */
300 void dhcppkt_init ( struct dhcp_packet *dhcppkt, struct dhcphdr *data,
301                     size_t len ) {
302         ref_init ( &dhcppkt->refcnt, NULL );
303         dhcppkt->dhcphdr = data;
304         dhcpopt_init ( &dhcppkt->options, &dhcppkt->dhcphdr->options,
305                        ( len - offsetof ( struct dhcphdr, options ) ),
306                        dhcpopt_no_realloc );
307         settings_init ( &dhcppkt->settings, &dhcppkt_settings_operations,
308                         &dhcppkt->refcnt, NULL );
309 }