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., 675 Mass Ave, Cambridge, MA 02139, USA.
19 FILE_LICENCE ( GPL2_OR_LATER
);
26 #include <gpxe/netdevice.h>
27 #include <gpxe/dhcp.h>
28 #include <gpxe/dhcpopts.h>
29 #include <gpxe/dhcppkt.h>
37 /****************************************************************************
39 * DHCP packet raw interface
44 * Calculate used length of an IPv4 field within a DHCP packet
47 * @v len Length of field
48 * @ret used Used length of field
50 static size_t used_len_ipv4 ( const void *data
, size_t len __unused
) {
51 const struct in_addr
*in
= data
;
53 return ( in
->s_addr
? sizeof ( *in
) : 0 );
57 * Calculate used length of a string field within a DHCP packet
60 * @v len Length of field
61 * @ret used Used length of field
63 static size_t used_len_string ( const void *data
, size_t len
) {
64 return strnlen ( data
, len
);
67 /** A dedicated field within a DHCP packet */
68 struct dhcp_packet_field
{
69 /** Settings tag number */
71 /** Offset within DHCP packet */
73 /** Length of field */
75 /** Calculate used length of field
78 * @v len Length of field
79 * @ret used Used length of field
81 size_t ( * used_len
) ( const void *data
, size_t len
);
84 /** Declare a dedicated field within a DHCP packet
86 * @v _tag Settings tag number
87 * @v _field Field name
88 * @v _used_len Function to calculate used length of field
90 #define DHCP_PACKET_FIELD( _tag, _field, _used_len ) { \
92 .offset = offsetof ( struct dhcphdr, _field ), \
93 .len = sizeof ( ( ( struct dhcphdr * ) 0 )->_field ), \
94 .used_len = _used_len, \
97 /** Dedicated fields within a DHCP packet */
98 static struct dhcp_packet_field dhcp_packet_fields
[] = {
99 DHCP_PACKET_FIELD ( DHCP_EB_YIADDR
, yiaddr
, used_len_ipv4
),
100 DHCP_PACKET_FIELD ( DHCP_EB_SIADDR
, siaddr
, used_len_ipv4
),
101 DHCP_PACKET_FIELD ( DHCP_TFTP_SERVER_NAME
, sname
, used_len_string
),
102 DHCP_PACKET_FIELD ( DHCP_BOOTFILE_NAME
, file
, used_len_string
),
106 * Get address of a DHCP packet field
108 * @v dhcphdr DHCP packet header
109 * @v field DHCP packet field
110 * @ret data Packet field data
112 static inline void * dhcp_packet_field ( struct dhcphdr
*dhcphdr
,
113 struct dhcp_packet_field
*field
) {
114 return ( ( ( void * ) dhcphdr
) + field
->offset
);
118 * Find DHCP packet field corresponding to settings tag number
120 * @v tag Settings tag number
121 * @ret field DHCP packet field, or NULL
123 static struct dhcp_packet_field
*
124 find_dhcp_packet_field ( unsigned int tag
) {
125 struct dhcp_packet_field
*field
;
128 for ( i
= 0 ; i
< ( sizeof ( dhcp_packet_fields
) /
129 sizeof ( dhcp_packet_fields
[0] ) ) ; i
++ ) {
130 field
= &dhcp_packet_fields
[i
];
131 if ( field
->tag
== tag
)
138 * Store value of DHCP packet setting
140 * @v dhcppkt DHCP packet
141 * @v tag Setting tag number
142 * @v data Setting data, or NULL to clear setting
143 * @v len Length of setting data
144 * @ret rc Return status code
146 int dhcppkt_store ( struct dhcp_packet
*dhcppkt
, unsigned int tag
,
147 const void *data
, size_t len
) {
148 struct dhcp_packet_field
*field
;
152 /* If this is a special field, fill it in */
153 if ( ( field
= find_dhcp_packet_field ( tag
) ) != NULL
) {
154 if ( len
> field
->len
)
156 field_data
= dhcp_packet_field ( dhcppkt
->dhcphdr
, field
);
157 memset ( field_data
, 0, field
->len
);
158 memcpy ( dhcp_packet_field ( dhcppkt
->dhcphdr
, field
),
160 /* Erase any equivalent option from the options block */
161 dhcpopt_store ( &dhcppkt
->options
, tag
, NULL
, 0 );
165 /* Otherwise, use the generic options block */
166 rc
= dhcpopt_store ( &dhcppkt
->options
, tag
, data
, len
);
168 /* Update our used-length field */
169 dhcppkt
->len
= ( offsetof ( struct dhcphdr
, options
) +
170 dhcppkt
->options
.len
);
176 * Fetch value of DHCP packet setting
178 * @v dhcppkt DHCP packet
179 * @v tag Setting tag number
180 * @v data Buffer to fill with setting data
181 * @v len Length of buffer
182 * @ret len Length of setting data, or negative error
184 int dhcppkt_fetch ( struct dhcp_packet
*dhcppkt
, unsigned int tag
,
185 void *data
, size_t len
) {
186 struct dhcp_packet_field
*field
;
188 size_t field_len
= 0;
190 /* Identify special field, if any */
191 if ( ( field
= find_dhcp_packet_field ( tag
) ) != NULL
) {
192 field_data
= dhcp_packet_field ( dhcppkt
->dhcphdr
, field
);
193 field_len
= field
->used_len ( field_data
, field
->len
);
196 /* Return special field, if it exists and is populated */
198 if ( len
> field_len
)
200 memcpy ( data
, field_data
, len
);
204 /* Otherwise, use the generic options block */
205 return dhcpopt_fetch ( &dhcppkt
->options
, tag
, data
, len
);
208 /****************************************************************************
210 * DHCP packet settings interface
215 * Store value of DHCP setting
217 * @v settings Settings block
218 * @v setting Setting to store
219 * @v data Setting data, or NULL to clear setting
220 * @v len Length of setting data
221 * @ret rc Return status code
223 static int dhcppkt_settings_store ( struct settings
*settings
,
224 struct setting
*setting
,
225 const void *data
, size_t len
) {
226 struct dhcp_packet
*dhcppkt
=
227 container_of ( settings
, struct dhcp_packet
, settings
);
229 return dhcppkt_store ( dhcppkt
, setting
->tag
, data
, len
);
233 * Fetch value of DHCP setting
235 * @v settings Settings block, or NULL to search all blocks
236 * @v setting Setting to fetch
237 * @v data Buffer to fill with setting data
238 * @v len Length of buffer
239 * @ret len Length of setting data, or negative error
241 static int dhcppkt_settings_fetch ( struct settings
*settings
,
242 struct setting
*setting
,
243 void *data
, size_t len
) {
244 struct dhcp_packet
*dhcppkt
=
245 container_of ( settings
, struct dhcp_packet
, settings
);
247 return dhcppkt_fetch ( dhcppkt
, setting
->tag
, data
, len
);
250 /** DHCP settings operations */
251 static struct settings_operations dhcppkt_settings_operations
= {
252 .store
= dhcppkt_settings_store
,
253 .fetch
= dhcppkt_settings_fetch
,
256 /****************************************************************************
263 * Initialise DHCP packet
265 * @v dhcppkt DHCP packet structure to fill in
266 * @v data DHCP packet raw data
267 * @v max_len Length of raw data buffer
269 * Initialise a DHCP packet structure from a data buffer containing a
272 void dhcppkt_init ( struct dhcp_packet
*dhcppkt
, struct dhcphdr
*data
,
274 dhcppkt
->dhcphdr
= data
;
275 dhcppkt
->max_len
= len
;
276 dhcpopt_init ( &dhcppkt
->options
, &dhcppkt
->dhcphdr
->options
,
277 ( len
- offsetof ( struct dhcphdr
, options
) ) );
278 dhcppkt
->len
= ( offsetof ( struct dhcphdr
, options
) +
279 dhcppkt
->options
.len
);
280 settings_init ( &dhcppkt
->settings
,
281 &dhcppkt_settings_operations
, &dhcppkt
->refcnt
,
282 DHCP_SETTINGS_NAME
, 0 );