00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 FILE_LICENCE ( GPL2_OR_LATER );
00020
00021 #include <stdint.h>
00022 #include <stdlib.h>
00023 #include <stdio.h>
00024 #include <errno.h>
00025 #include <string.h>
00026 #include <gpxe/netdevice.h>
00027 #include <gpxe/dhcp.h>
00028 #include <gpxe/dhcpopts.h>
00029 #include <gpxe/dhcppkt.h>
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050 static size_t used_len_ipv4 ( const void *data, size_t len __unused ) {
00051 const struct in_addr *in = data;
00052
00053 return ( in->s_addr ? sizeof ( *in ) : 0 );
00054 }
00055
00056
00057
00058
00059
00060
00061
00062
00063 static size_t used_len_string ( const void *data, size_t len ) {
00064 return strnlen ( data, len );
00065 }
00066
00067
00068 struct dhcp_packet_field {
00069
00070 unsigned int tag;
00071
00072 uint16_t offset;
00073
00074 uint16_t len;
00075
00076
00077
00078
00079
00080
00081 size_t ( * used_len ) ( const void *data, size_t len );
00082 };
00083
00084
00085
00086
00087
00088
00089
00090 #define DHCP_PACKET_FIELD( _tag, _field, _used_len ) { \
00091 .tag = (_tag), \
00092 .offset = offsetof ( struct dhcphdr, _field ), \
00093 .len = sizeof ( ( ( struct dhcphdr * ) 0 )->_field ), \
00094 .used_len = _used_len, \
00095 }
00096
00097
00098 static struct dhcp_packet_field dhcp_packet_fields[] = {
00099 DHCP_PACKET_FIELD ( DHCP_EB_YIADDR, yiaddr, used_len_ipv4 ),
00100 DHCP_PACKET_FIELD ( DHCP_EB_SIADDR, siaddr, used_len_ipv4 ),
00101 DHCP_PACKET_FIELD ( DHCP_TFTP_SERVER_NAME, sname, used_len_string ),
00102 DHCP_PACKET_FIELD ( DHCP_BOOTFILE_NAME, file, used_len_string ),
00103 };
00104
00105
00106
00107
00108
00109
00110
00111
00112 static inline void * dhcp_packet_field ( struct dhcphdr *dhcphdr,
00113 struct dhcp_packet_field *field ) {
00114 return ( ( ( void * ) dhcphdr ) + field->offset );
00115 }
00116
00117
00118
00119
00120
00121
00122
00123 static struct dhcp_packet_field *
00124 find_dhcp_packet_field ( unsigned int tag ) {
00125 struct dhcp_packet_field *field;
00126 unsigned int i;
00127
00128 for ( i = 0 ; i < ( sizeof ( dhcp_packet_fields ) /
00129 sizeof ( dhcp_packet_fields[0] ) ) ; i++ ) {
00130 field = &dhcp_packet_fields[i];
00131 if ( field->tag == tag )
00132 return field;
00133 }
00134 return NULL;
00135 }
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146 int dhcppkt_store ( struct dhcp_packet *dhcppkt, unsigned int tag,
00147 const void *data, size_t len ) {
00148 struct dhcp_packet_field *field;
00149 void *field_data;
00150 int rc;
00151
00152
00153 if ( ( field = find_dhcp_packet_field ( tag ) ) != NULL ) {
00154 if ( len > field->len )
00155 return -ENOSPC;
00156 field_data = dhcp_packet_field ( dhcppkt->dhcphdr, field );
00157 memset ( field_data, 0, field->len );
00158 memcpy ( dhcp_packet_field ( dhcppkt->dhcphdr, field ),
00159 data, len );
00160
00161 dhcpopt_store ( &dhcppkt->options, tag, NULL, 0 );
00162 return 0;
00163 }
00164
00165
00166 rc = dhcpopt_store ( &dhcppkt->options, tag, data, len );
00167
00168
00169 dhcppkt->len = ( offsetof ( struct dhcphdr, options ) +
00170 dhcppkt->options.len );
00171
00172 return rc;
00173 }
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184 int dhcppkt_fetch ( struct dhcp_packet *dhcppkt, unsigned int tag,
00185 void *data, size_t len ) {
00186 struct dhcp_packet_field *field;
00187 void *field_data;
00188 size_t field_len = 0;
00189
00190
00191 if ( ( field = find_dhcp_packet_field ( tag ) ) != NULL ) {
00192 field_data = dhcp_packet_field ( dhcppkt->dhcphdr, field );
00193 field_len = field->used_len ( field_data, field->len );
00194 }
00195
00196
00197 if ( field_len ) {
00198 if ( len > field_len )
00199 len = field_len;
00200 memcpy ( data, field_data, len );
00201 return field_len;
00202 }
00203
00204
00205 return dhcpopt_fetch ( &dhcppkt->options, tag, data, len );
00206 }
00207
00208
00209
00210
00211
00212
00213
00214
00215
00216
00217
00218
00219
00220
00221
00222
00223 static int dhcppkt_settings_store ( struct settings *settings,
00224 struct setting *setting,
00225 const void *data, size_t len ) {
00226 struct dhcp_packet *dhcppkt =
00227 container_of ( settings, struct dhcp_packet, settings );
00228
00229 return dhcppkt_store ( dhcppkt, setting->tag, data, len );
00230 }
00231
00232
00233
00234
00235
00236
00237
00238
00239
00240
00241 static int dhcppkt_settings_fetch ( struct settings *settings,
00242 struct setting *setting,
00243 void *data, size_t len ) {
00244 struct dhcp_packet *dhcppkt =
00245 container_of ( settings, struct dhcp_packet, settings );
00246
00247 return dhcppkt_fetch ( dhcppkt, setting->tag, data, len );
00248 }
00249
00250
00251 static struct settings_operations dhcppkt_settings_operations = {
00252 .store = dhcppkt_settings_store,
00253 .fetch = dhcppkt_settings_fetch,
00254 };
00255
00256
00257
00258
00259
00260
00261
00262
00263
00264
00265
00266
00267
00268
00269
00270
00271
00272 void dhcppkt_init ( struct dhcp_packet *dhcppkt, struct dhcphdr *data,
00273 size_t len ) {
00274 dhcppkt->dhcphdr = data;
00275 dhcppkt->max_len = len;
00276 dhcpopt_init ( &dhcppkt->options, &dhcppkt->dhcphdr->options,
00277 ( len - offsetof ( struct dhcphdr, options ) ) );
00278 dhcppkt->len = ( offsetof ( struct dhcphdr, options ) +
00279 dhcppkt->options.len );
00280 settings_init ( &dhcppkt->settings,
00281 &dhcppkt_settings_operations, &dhcppkt->refcnt,
00282 DHCP_SETTINGS_NAME, 0 );
00283 }