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