summaryrefslogtreecommitdiff
path: root/networking/udhcp
diff options
context:
space:
mode:
Diffstat (limited to 'networking/udhcp')
-rw-r--r--networking/udhcp/common.c96
-rw-r--r--networking/udhcp/common.h8
2 files changed, 62 insertions, 42 deletions
diff --git a/networking/udhcp/common.c b/networking/udhcp/common.c
index 16bf697..20d843b 100644
--- a/networking/udhcp/common.c
+++ b/networking/udhcp/common.c
@@ -15,7 +15,7 @@ const uint8_t MAC_BCAST_ADDR[6] ALIGN2 = {
};
#if ENABLE_UDHCPC || ENABLE_UDHCPD
-/* Supported options are easily added here.
+/* Supported options are easily added here, they need to be sorted.
* See RFC2132 for more options.
* OPTION_REQ: these options are requested by udhcpc (unless -o).
*/
@@ -222,79 +222,91 @@ unsigned FAST_FUNC udhcp_option_idx(const char *name, const char *option_strings
}
}
-/* Get an option with bounds checking (warning, result is not aligned) */
-uint8_t* FAST_FUNC udhcp_get_option(struct dhcp_packet *packet, int code)
+/* Initialize state to be used between subsequent udhcp_scan_options calls */
+void FAST_FUNC init_scan_state(struct dhcp_packet *packet, struct dhcp_scan_state *scan_state)
+{
+ scan_state->overload = 0;
+ scan_state->rem = sizeof(packet->options);
+ scan_state->optionptr = packet->options;
+}
+
+/* Iterate over packet's options, each call returning the next option.
+ * scan_state needs to be initialized with init_scan_state beforehand.
+ * Warning, result is not aligned. */
+uint8_t* FAST_FUNC udhcp_scan_options(struct dhcp_packet *packet, struct dhcp_scan_state *scan_state)
{
- uint8_t *optionptr;
int len;
- int rem;
- int overload = 0;
enum {
FILE_FIELD101 = FILE_FIELD * 0x101,
SNAME_FIELD101 = SNAME_FIELD * 0x101,
};
/* option bytes: [code][len][data1][data2]..[dataLEN] */
- optionptr = packet->options;
- rem = sizeof(packet->options);
while (1) {
- if (rem <= 0) {
+ if (scan_state->rem <= 0) {
complain:
bb_simple_error_msg("bad packet, malformed option field");
return NULL;
}
/* DHCP_PADDING and DHCP_END have no [len] byte */
- if (optionptr[OPT_CODE] == DHCP_PADDING) {
- rem--;
- optionptr++;
+ if (scan_state->optionptr[OPT_CODE] == DHCP_PADDING) {
+ scan_state->rem--;
+ scan_state->optionptr++;
continue;
}
- if (optionptr[OPT_CODE] == DHCP_END) {
- if ((overload & FILE_FIELD101) == FILE_FIELD) {
+ if (scan_state->optionptr[OPT_CODE] == DHCP_END) {
+ if ((scan_state->overload & FILE_FIELD101) == FILE_FIELD) {
/* can use packet->file, and didn't look at it yet */
- overload |= FILE_FIELD101; /* "we looked at it" */
- optionptr = packet->file;
- rem = sizeof(packet->file);
+ scan_state->overload |= FILE_FIELD101; /* "we looked at it" */
+ scan_state->optionptr = packet->file;
+ scan_state->rem = sizeof(packet->file);
continue;
}
- if ((overload & SNAME_FIELD101) == SNAME_FIELD) {
+ if ((scan_state->overload & SNAME_FIELD101) == SNAME_FIELD) {
/* can use packet->sname, and didn't look at it yet */
- overload |= SNAME_FIELD101; /* "we looked at it" */
- optionptr = packet->sname;
- rem = sizeof(packet->sname);
+ scan_state->overload |= SNAME_FIELD101; /* "we looked at it" */
+ scan_state->optionptr = packet->sname;
+ scan_state->rem = sizeof(packet->sname);
continue;
}
break;
}
- if (rem <= OPT_LEN)
+ if (scan_state->rem <= OPT_LEN)
goto complain; /* complain and return NULL */
- len = 2 + optionptr[OPT_LEN];
- rem -= len;
- if (rem < 0)
+ len = 2 + scan_state->optionptr[OPT_LEN];
+ scan_state->rem -= len;
+ /* So far no valid option with length 0 known. */
+ if (scan_state->rem < 0 || scan_state->optionptr[OPT_LEN] == 0)
goto complain; /* complain and return NULL */
- if (optionptr[OPT_CODE] == code) {
- if (optionptr[OPT_LEN] == 0) {
- /* So far no valid option with length 0 known.
- * Having this check means that searching
- * for DHCP_MESSAGE_TYPE need not worry
- * that returned pointer might be unsafe
- * to dereference.
- */
- goto complain; /* complain and return NULL */
- }
- log_option("option found", optionptr);
- return optionptr + OPT_DATA;
+ if (scan_state->optionptr[OPT_CODE] == DHCP_OPTION_OVERLOAD) {
+ if (len >= 3)
+ scan_state->overload |= scan_state->optionptr[OPT_DATA];
+ } else {
+ uint8_t *return_ptr = scan_state->optionptr;
+ scan_state->optionptr += len;
+ return return_ptr;
}
+ scan_state->optionptr += len;
+ }
- if (optionptr[OPT_CODE] == DHCP_OPTION_OVERLOAD) {
- if (len >= 3)
- overload |= optionptr[OPT_DATA];
- /* fall through */
+ return NULL;
+}
+
+/* Get an option with bounds checking (warning, result is not aligned) */
+uint8_t* FAST_FUNC udhcp_get_option(struct dhcp_packet *packet, int code)
+{
+ uint8_t *optptr;
+ struct dhcp_scan_state scan_state;
+
+ init_scan_state(packet, &scan_state);
+ while ((optptr = udhcp_scan_options(packet, &scan_state)) != NULL) {
+ if (optptr[OPT_CODE] == code) {
+ log_option("option found", optptr);
+ return optptr + OPT_DATA;
}
- optionptr += len;
}
/* log3 because udhcpc uses it a lot - very noisy */
diff --git a/networking/udhcp/common.h b/networking/udhcp/common.h
index 6214db0..81c1dcb 100644
--- a/networking/udhcp/common.h
+++ b/networking/udhcp/common.h
@@ -107,6 +107,12 @@ enum {
OPTION_LIST = 0x20,
};
+struct dhcp_scan_state {
+ int overload;
+ int rem;
+ uint8_t *optionptr;
+};
+
/* DHCP option codes (partial list). See RFC 2132 and
* http://www.iana.org/assignments/bootp-dhcp-parameters/
* Commented out options are handled by common option machinery,
@@ -206,6 +212,8 @@ extern const uint8_t dhcp_option_lengths[] ALIGN1;
unsigned FAST_FUNC udhcp_option_idx(const char *name, const char *option_strings);
+void init_scan_state(struct dhcp_packet *packet, struct dhcp_scan_state *scan_state) FAST_FUNC;
+uint8_t *udhcp_scan_options(struct dhcp_packet *packet, struct dhcp_scan_state *scan_state) FAST_FUNC;
uint8_t *udhcp_get_option(struct dhcp_packet *packet, int code) FAST_FUNC;
/* Same as above + ensures that option length is 4 bytes
* (returns NULL if size is different)