|
|
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.