[zbin] Fix check for existence of most recent output byte
[ipxe.git] / 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 }