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
20 FILE_LICENCE ( GPL2_OR_LATER );
27 #include <ipxe/netdevice.h>
28 #include <ipxe/dhcp.h>
29 #include <ipxe/dhcpopts.h>
30 #include <ipxe/dhcppkt.h>
38 /****************************************************************************
40 * DHCP packet raw interface
45 * Calculate used length of an IPv4 field within a DHCP packet
48 * @v len Length of field
49 * @ret used Used length of field
51 static size_t used_len_ipv4 ( const void *data, size_t len __unused ) {
52 const struct in_addr *in = data;
54 return ( in->s_addr ? sizeof ( *in ) : 0 );
58 * Calculate used length of a string field within a DHCP packet
61 * @v len Length of field
62 * @ret used Used length of field
64 static size_t used_len_string ( const void *data, size_t len ) {
65 return strnlen ( data, len );
68 /** A dedicated field within a DHCP packet */
69 struct dhcp_packet_field {
70 /** Settings tag number */
72 /** Offset within DHCP packet */
74 /** Length of field */
76 /** Calculate used length of field
79 * @v len Length of field
80 * @ret used Used length of field
82 size_t ( * used_len ) ( const void *data, size_t len );
85 /** Declare a dedicated field within a DHCP packet
87 * @v _tag Settings tag number
88 * @v _field Field name
89 * @v _used_len Function to calculate used length of field
91 #define DHCP_PACKET_FIELD( _tag, _field, _used_len ) { \
93 .offset = offsetof ( struct dhcphdr, _field ), \
94 .len = sizeof ( ( ( struct dhcphdr * ) 0 )->_field ), \
95 .used_len = _used_len, \
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 ),
107 * Get address of a DHCP packet field
109 * @v dhcphdr DHCP packet header
110 * @v field DHCP packet field
111 * @ret data Packet field data
113 static inline void * dhcp_packet_field ( struct dhcphdr *dhcphdr,
114 struct dhcp_packet_field *field ) {
115 return ( ( ( void * ) dhcphdr ) + field->offset );
119 * Find DHCP packet field corresponding to settings tag number
121 * @v tag Settings tag number
122 * @ret field DHCP packet field, or NULL
124 static struct dhcp_packet_field *
125 find_dhcp_packet_field ( unsigned int tag ) {
126 struct dhcp_packet_field *field;
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 )
139 * Check applicability of DHCP setting
141 * @v dhcppkt DHCP packet
142 * @v tag Setting tag number
143 * @ret applies Setting applies within this settings block
145 static int dhcppkt_applies ( struct dhcp_packet *dhcppkt __unused,
148 return dhcpopt_applies ( tag );
152 * Store value of DHCP packet setting
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
160 int dhcppkt_store ( struct dhcp_packet *dhcppkt, unsigned int tag,
161 const void *data, size_t len ) {
162 struct dhcp_packet_field *field;
165 /* If this is a special field, fill it in */
166 if ( ( field = find_dhcp_packet_field ( tag ) ) != NULL ) {
167 if ( len > field->len )
169 field_data = dhcp_packet_field ( dhcppkt->dhcphdr, field );
170 memset ( field_data, 0, field->len );
171 memcpy ( dhcp_packet_field ( dhcppkt->dhcphdr, field ),
173 /* Erase any equivalent option from the options block */
174 dhcpopt_store ( &dhcppkt->options, tag, NULL, 0 );
178 /* Otherwise, use the generic options block */
179 return dhcpopt_store ( &dhcppkt->options, tag, data, len );
183 * Fetch value of DHCP packet setting
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
191 int dhcppkt_fetch ( struct dhcp_packet *dhcppkt, unsigned int tag,
192 void *data, size_t len ) {
193 struct dhcp_packet_field *field;
195 size_t field_len = 0;
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 );
203 /* Return special field, if it exists and is populated */
205 if ( len > field_len )
207 memcpy ( data, field_data, len );
211 /* Otherwise, use the generic options block */
212 return dhcpopt_fetch ( &dhcppkt->options, tag, data, len );
215 /****************************************************************************
217 * DHCP packet settings interface
222 * Check applicability of DHCP setting
224 * @v settings Settings block
226 * @ret applies Setting applies within this settings block
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 );
233 return ( ( setting->scope == NULL ) &&
234 dhcppkt_applies ( dhcppkt, setting->tag ) );
238 * Store value of DHCP setting
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
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 );
252 return dhcppkt_store ( dhcppkt, setting->tag, data, len );
256 * Fetch value of DHCP setting
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
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 );
270 return dhcppkt_fetch ( dhcppkt, setting->tag, data, len );
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,
280 /****************************************************************************
287 * Initialise DHCP packet
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
293 * Initialise a DHCP packet structure from a data buffer containing a
296 void dhcppkt_init ( struct dhcp_packet *dhcppkt, struct dhcphdr *data,
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 );