2 * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
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.
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.
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
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.
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL
);
31 #include <ipxe/netdevice.h>
32 #include <ipxe/dhcp.h>
33 #include <ipxe/dhcpopts.h>
34 #include <ipxe/dhcppkt.h>
42 /****************************************************************************
44 * DHCP packet raw interface
49 * Calculate used length of an IPv4 field within a DHCP packet
52 * @v len Length of field
53 * @ret used Used length of field
55 static size_t used_len_ipv4 ( const void *data
, size_t len __unused
) {
56 const struct in_addr
*in
= data
;
58 return ( in
->s_addr ?
sizeof ( *in
) : 0 );
62 * Calculate used length of a string field within a DHCP packet
65 * @v len Length of field
66 * @ret used Used length of field
68 static size_t used_len_string ( const void *data
, size_t len
) {
69 return strnlen ( data
, len
);
72 /** A dedicated field within a DHCP packet */
73 struct dhcp_packet_field
{
74 /** Settings tag number */
76 /** Offset within DHCP packet */
78 /** Length of field */
80 /** Calculate used length of field
83 * @v len Length of field
84 * @ret used Used length of field
86 size_t ( * used_len
) ( const void *data
, size_t len
);
89 /** Declare a dedicated field within a DHCP packet
91 * @v _tag Settings tag number
92 * @v _field Field name
93 * @v _used_len Function to calculate used length of field
95 #define DHCP_PACKET_FIELD( _tag, _field, _used_len ) { \
97 .offset = offsetof ( struct dhcphdr, _field ), \
98 .len = sizeof ( ( ( struct dhcphdr * ) 0 )->_field ), \
99 .used_len = _used_len, \
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
),
111 * Get address of a DHCP packet field
113 * @v dhcphdr DHCP packet header
114 * @v field DHCP packet field
115 * @ret data Packet field data
117 static inline void * dhcp_packet_field ( struct dhcphdr
*dhcphdr
,
118 struct dhcp_packet_field
*field
) {
119 return ( ( ( void * ) dhcphdr
) + field
->offset
);
123 * Find DHCP packet field corresponding to settings tag number
125 * @v tag Settings tag number
126 * @ret field DHCP packet field, or NULL
128 static struct dhcp_packet_field
*
129 find_dhcp_packet_field ( unsigned int tag
) {
130 struct dhcp_packet_field
*field
;
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
)
143 * Check applicability of DHCP setting
145 * @v dhcppkt DHCP packet
146 * @v tag Setting tag number
147 * @ret applies Setting applies within this settings block
149 static int dhcppkt_applies ( struct dhcp_packet
*dhcppkt __unused
,
152 return dhcpopt_applies ( tag
);
156 * Store value of DHCP packet setting
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
164 int dhcppkt_store ( struct dhcp_packet
*dhcppkt
, unsigned int tag
,
165 const void *data
, size_t len
) {
166 struct dhcp_packet_field
*field
;
169 /* If this is a special field, fill it in */
170 if ( ( field
= find_dhcp_packet_field ( tag
) ) != NULL
) {
171 if ( len
> field
->len
)
173 field_data
= dhcp_packet_field ( dhcppkt
->dhcphdr
, field
);
174 memset ( field_data
, 0, field
->len
);
175 memcpy ( dhcp_packet_field ( dhcppkt
->dhcphdr
, field
),
177 /* Erase any equivalent option from the options block */
178 dhcpopt_store ( &dhcppkt
->options
, tag
, NULL
, 0 );
182 /* Otherwise, use the generic options block */
183 return dhcpopt_store ( &dhcppkt
->options
, tag
, data
, len
);
187 * Fetch value of DHCP packet setting
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
195 int dhcppkt_fetch ( struct dhcp_packet
*dhcppkt
, unsigned int tag
,
196 void *data
, size_t len
) {
197 struct dhcp_packet_field
*field
;
199 size_t field_len
= 0;
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
);
207 /* Return special field, if it exists and is populated */
209 if ( len
> field_len
)
211 memcpy ( data
, field_data
, len
);
215 /* Otherwise, use the generic options block */
216 return dhcpopt_fetch ( &dhcppkt
->options
, tag
, data
, len
);
219 /****************************************************************************
221 * DHCP packet settings interface
226 * Check applicability of DHCP setting
228 * @v settings Settings block
230 * @ret applies Setting applies within this settings block
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
);
237 return ( ( setting
->scope
== NULL
) &&
238 dhcppkt_applies ( dhcppkt
, setting
->tag
) );
242 * Store value of DHCP setting
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
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
);
256 return dhcppkt_store ( dhcppkt
, setting
->tag
, data
, len
);
260 * Fetch value of DHCP setting
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
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
);
274 return dhcppkt_fetch ( dhcppkt
, setting
->tag
, data
, len
);
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
,
284 /****************************************************************************
291 * Initialise DHCP packet
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
297 * Initialise a DHCP packet structure from a data buffer containing a
300 void dhcppkt_init ( struct dhcp_packet
*dhcppkt
, struct dhcphdr
*data
,
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
);