You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

dhcpopts.c 6.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. /*
  2. * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
  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. #include <stdint.h>
  19. #include <byteswap.h>
  20. #include <errno.h>
  21. #include <malloc.h>
  22. #include <assert.h>
  23. #include <gpxe/list.h>
  24. #include <gpxe/dhcp.h>
  25. /** @file
  26. *
  27. * DHCP options
  28. *
  29. */
  30. /** List of registered DHCP option blocks */
  31. static LIST_HEAD ( option_blocks );
  32. /**
  33. * Obtain value of a numerical DHCP option
  34. *
  35. * @v option DHCP option, or NULL
  36. * @v value Unsigned long for storing the result
  37. * @ret rc Return status code
  38. *
  39. * Parses the numerical value from a DHCP option, if present. It is
  40. * permitted to call dhcp_num_option() with @c option set to NULL; in
  41. * this case the result value will not be modified and an error will
  42. * be returned.
  43. *
  44. * The caller does not specify the size of the DHCP option data; this
  45. * is implied by the length field stored within the DHCP option
  46. * itself.
  47. */
  48. int dhcp_num_option ( struct dhcp_option *option, unsigned long *value ) {
  49. uint8_t *data;
  50. unsigned long tmp = 0;
  51. if ( ! option )
  52. return -EINVAL;
  53. /* This is actually smaller code than using htons() etc., and
  54. * will also cope well with malformed options (such as
  55. * zero-length options).
  56. */
  57. for ( data = option->data.bytes ;
  58. data < ( option->data.bytes + option->len ) ; data++ )
  59. tmp = ( ( tmp << 8 ) | *data );
  60. *value = tmp;
  61. return 0;
  62. }
  63. /**
  64. * Calculate length of a DHCP option
  65. *
  66. * @v option DHCP option
  67. * @ret len Length (including tag and length field)
  68. */
  69. static inline unsigned int dhcp_option_len ( struct dhcp_option *option ) {
  70. if ( ( option->tag == DHCP_END ) || ( option->tag == DHCP_PAD ) ) {
  71. return 1;
  72. } else {
  73. return ( option->len + 2 );
  74. }
  75. }
  76. /**
  77. * Find DHCP option within block of raw data
  78. *
  79. * @v tag DHCP option tag to search for
  80. * @v data Data block
  81. * @v len Length of data block
  82. * @ret option DHCP option, or NULL if not found
  83. *
  84. * Searches for the DHCP option matching the specified tag within the
  85. * block of data. Encapsulated options may be searched for by using
  86. * DHCP_ENCAP_OPT() to construct the tag value.
  87. *
  88. * This routine is designed to be paranoid. It does not assume that
  89. * the option data is well-formatted, and so must guard against flaws
  90. * such as options missing a @c DHCP_END terminator, or options whose
  91. * length would take them beyond the end of the data block.
  92. *
  93. * Searching for @c DHCP_PAD or @c DHCP_END tags, or using either @c
  94. * DHCP_PAD or @c DHCP_END as the encapsulator when constructing the
  95. * tag via DHCP_ENCAP_OPT() will produce undefined behaviour.
  96. */
  97. static struct dhcp_option * find_dhcp_option_raw ( unsigned int tag,
  98. void *data, size_t len ) {
  99. struct dhcp_option *option = data;
  100. ssize_t remaining = len;
  101. unsigned int option_len;
  102. assert ( tag != DHCP_PAD );
  103. assert ( tag != DHCP_END );
  104. assert ( DHCP_ENCAPSULATOR ( tag ) != DHCP_END );
  105. while ( remaining ) {
  106. /* Check for explicit end marker */
  107. if ( option->tag == DHCP_END )
  108. break;
  109. /* Calculate length of this option. Abort processing
  110. * if the length is malformed (i.e. takes us beyond
  111. * the end of the data block).
  112. */
  113. option_len = dhcp_option_len ( option );
  114. remaining -= option_len;
  115. if ( remaining < 0 )
  116. break;
  117. /* Check for matching tag */
  118. if ( option->tag == tag )
  119. return option;
  120. /* Check for start of matching encapsulation block */
  121. if ( DHCP_ENCAPSULATOR ( tag ) &&
  122. ( option->tag == DHCP_ENCAPSULATOR ( tag ) ) ) {
  123. /* Search within encapsulated option block */
  124. return find_dhcp_option_raw ( DHCP_ENCAPSULATED( tag ),
  125. &option->data,
  126. option->len );
  127. }
  128. option = ( ( ( void * ) option ) + option_len );
  129. }
  130. return NULL;
  131. }
  132. /**
  133. * Find DHCP option within all registered DHCP options blocks
  134. *
  135. * @v tag DHCP option tag to search for
  136. * @ret option DHCP option, or NULL if not found
  137. *
  138. * Searches within all registered DHCP option blocks for the specified
  139. * tag. Encapsulated options may be searched for by using
  140. * DHCP_ENCAP_OPT() to construct the tag value.
  141. */
  142. struct dhcp_option * find_dhcp_option ( unsigned int tag ) {
  143. struct dhcp_option_block *options;
  144. struct dhcp_option *option;
  145. list_for_each_entry ( options, &option_blocks, list ) {
  146. if ( ( option = find_dhcp_option_raw ( tag, options->data,
  147. options->len ) ) )
  148. return option;
  149. }
  150. return NULL;
  151. }
  152. /**
  153. * Register DHCP option block
  154. *
  155. * @v options DHCP option block
  156. *
  157. * Register a block of DHCP options
  158. */
  159. void register_dhcp_options ( struct dhcp_option_block *options ) {
  160. list_add ( &options->list, &option_blocks );
  161. }
  162. /**
  163. * Unregister DHCP option block
  164. *
  165. * @v options DHCP option block
  166. */
  167. void unregister_dhcp_options ( struct dhcp_option_block *options ) {
  168. list_del ( &options->list );
  169. }
  170. /**
  171. * Allocate space for a block of DHCP options
  172. *
  173. * @v len Maximum length of option block
  174. * @ret options Option block, or NULL
  175. *
  176. * Creates a new DHCP option block and populates it with an empty
  177. * options list. This call does not register the options block.
  178. */
  179. struct dhcp_option_block * alloc_dhcp_options ( size_t len ) {
  180. struct dhcp_option_block *options;
  181. struct dhcp_option *option;
  182. options = malloc ( sizeof ( *options ) + len );
  183. if ( options ) {
  184. options->data = ( ( void * ) options + sizeof ( *options ) );
  185. options->len = len;
  186. if ( len ) {
  187. option = options->data;
  188. option->tag = DHCP_END;
  189. }
  190. }
  191. return options;
  192. }
  193. /**
  194. * Free DHCP options block
  195. *
  196. * @v options Option block
  197. */
  198. void free_dhcp_options ( struct dhcp_option_block *options ) {
  199. free ( options );
  200. }