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