2 * Copyright (c) 2008 Nicira Networks.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at:
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <arpa/inet.h>
25 #include "dynamic-string.h"
28 #define THIS_MODULE VLM_dhcp
31 /* Information about a DHCP argument type. */
33 const char *name; /* Name. */
34 size_t size; /* Number of bytes per argument. */
37 static struct arg_type types[] = {
38 #define DHCP_ARG(NAME, SIZE) [DHCP_ARG_##NAME] = {#NAME, SIZE},
43 /* Information about a DHCP option. */
45 const char *name; /* Name. */
46 enum dhcp_arg_type type; /* Argument type. */
47 size_t min_args; /* Minimum number of arguments. */
48 size_t max_args; /* Maximum number of arguments. */
51 static const struct option_class *
52 get_option_class(int code)
54 static struct option_class classes[DHCP_N_OPTIONS];
55 static bool init = false;
60 #define DHCP_OPT(NAME, CODE, TYPE, MIN, MAX) \
61 classes[CODE].name = #NAME; \
62 classes[CODE].type = DHCP_ARG_##TYPE; \
63 classes[CODE].min_args = MIN; \
64 classes[CODE].max_args = MAX;
68 for (i = 0; i < DHCP_N_OPTIONS; i++) {
69 if (!classes[i].name) {
70 classes[i].name = xasprintf("option-%d", i);
71 classes[i].type = DHCP_ARG_UINT8;
72 classes[i].min_args = 0;
73 classes[i].max_args = SIZE_MAX;
77 assert(code >= 0 && code < DHCP_N_OPTIONS);
78 return &classes[code];
81 /* A single (bad) DHCP message can in theory dump out many, many log messages,
82 * especially at high logging levels, so the burst size is set quite high
83 * here to avoid missing useful information. */
84 struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(60, 600);
86 static void copy_data(struct dhcp_msg *);
89 dhcp_type_name(enum dhcp_msg_type type)
92 #define DHCP_MSG(NAME, VALUE) case NAME: return #NAME;
96 return "<<unknown DHCP message type>>";
99 /* Initializes 'msg' as a DHCP message. The message should be freed with
100 * dhcp_msg_uninit() when it is no longer needed. */
102 dhcp_msg_init(struct dhcp_msg *msg)
104 memset(msg, 0, sizeof *msg);
107 /* Frees the contents of 'msg'. The caller is responsible for freeing 'msg',
110 dhcp_msg_uninit(struct dhcp_msg *msg)
117 /* Initializes 'dst' as a copy of 'src'. 'dst' (and 'src') should be freed
118 * with dhcp_msg_uninit() when it is no longer needed. */
120 dhcp_msg_copy(struct dhcp_msg *dst, const struct dhcp_msg *src)
123 dst->data_allocated = src->data_used;
125 dst->data = xmalloc(dst->data_allocated);
130 prealloc_data(struct dhcp_msg *msg, size_t n)
132 size_t needed = msg->data_used + n;
133 if (needed > msg->data_allocated) {
134 uint8_t *old_data = msg->data;
135 msg->data_allocated = MAX(needed * 2, 64);
136 msg->data = xmalloc(msg->data_allocated);
145 append_data(struct dhcp_msg *msg, const void *data, size_t n)
147 uint8_t *p = &msg->data[msg->data_used];
154 copy_data(struct dhcp_msg *msg)
159 for (code = 0; code < DHCP_N_OPTIONS; code++) {
160 struct dhcp_option *opt = &msg->options[code];
162 assert(msg->data_used + opt->n <= msg->data_allocated);
163 opt->data = append_data(msg, opt->data, opt->n);
168 /* Appends the 'n' bytes in 'data' to the DHCP option in 'msg' represented by
169 * 'code' (which must be in the range 0...DHCP_N_OPTIONS). */
171 dhcp_msg_put(struct dhcp_msg *msg, int code,
172 const void *data, size_t n)
174 struct dhcp_option *opt;
175 if (code == DHCP_CODE_PAD || code == DHCP_CODE_END) {
179 opt = &msg->options[code];
180 prealloc_data(msg, n + opt->n);
182 if (&msg->data[msg->data_used - opt->n] != opt->data) {
183 opt->data = append_data(msg, opt->data, opt->n);
185 append_data(msg, data, n);
187 opt->data = append_data(msg, data, n);
192 /* Appends the boolean value 'b', as a octet with value 0 (false) or 1 (true),
193 * to the DHCP option in 'msg' represented by 'code' (which must be in the
194 * range 0...DHCP_N_OPTIONS). */
196 dhcp_msg_put_bool(struct dhcp_msg *msg, int code, bool b_)
199 dhcp_msg_put(msg, code, &b, 1);
202 /* Appends the number of seconds 'secs', as a 32-bit number in network byte
203 * order, to the DHCP option in 'msg' represented by 'code' (which must be in
204 * the range 0...DHCP_N_OPTIONS). */
206 dhcp_msg_put_secs(struct dhcp_msg *msg, int code, uint32_t secs_)
208 uint32_t secs = htonl(secs_);
209 dhcp_msg_put(msg, code, &secs, sizeof secs);
212 /* Appends the IP address 'ip', as a 32-bit number in network byte order, to
213 * the DHCP option in 'msg' represented by 'code' (which must be in the range
214 * 0...DHCP_N_OPTIONS). */
216 dhcp_msg_put_ip(struct dhcp_msg *msg, int code, uint32_t ip)
218 dhcp_msg_put(msg, code, &ip, sizeof ip);
221 /* Appends the ASCII string 'string', to the DHCP option in 'msg' represented
222 * by 'code' (which must be in the range 0...DHCP_N_OPTIONS). */
224 dhcp_msg_put_string(struct dhcp_msg *msg, int code, const char *string)
226 dhcp_msg_put(msg, code, string, strlen(string));
229 /* Appends octet 'x' to DHCP option in 'msg' represented by 'code' (which must
230 * be in the range 0...DHCP_N_OPTIONS). */
232 dhcp_msg_put_uint8(struct dhcp_msg *msg, int code, uint8_t x)
234 dhcp_msg_put(msg, code, &x, sizeof x);
237 /* Appends the 'n' octets in 'data' to DHCP option in 'msg' represented by
238 * 'code' (which must be in the range 0...DHCP_N_OPTIONS). */
239 void dhcp_msg_put_uint8_array(struct dhcp_msg *msg, int code,
240 const uint8_t data[], size_t n)
242 dhcp_msg_put(msg, code, data, n);
245 /* Appends the 16-bit value in 'x', in network byte order, to DHCP option in
246 * 'msg' represented by 'code' (which must be in the range
247 * 0...DHCP_N_OPTIONS). */
249 dhcp_msg_put_uint16(struct dhcp_msg *msg, int code, uint16_t x_)
251 uint16_t x = htons(x_);
252 dhcp_msg_put(msg, code, &x, sizeof x);
256 /* Appends the 'n' 16-bit values in 'data', in network byte order, to DHCP
257 * option in 'msg' represented by 'code' (which must be in the range
258 * 0...DHCP_N_OPTIONS). */
260 dhcp_msg_put_uint16_array(struct dhcp_msg *msg, int code,
261 const uint16_t data[], size_t n)
265 for (i = 0; i < n; i++) {
266 dhcp_msg_put_uint16(msg, code, data[i]);
270 /* Returns a pointer to the 'size' bytes starting at byte offset 'offset' in
271 * the DHCP option in 'msg' represented by 'code' (which must be in the range
272 * 0...DHCP_N_OPTIONS). If the option has fewer than 'offset + size' bytes,
273 * returns a null pointer. */
275 dhcp_msg_get(const struct dhcp_msg *msg, int code,
276 size_t offset, size_t size)
278 const struct dhcp_option *opt = &msg->options[code];
279 return offset + size <= opt->n ? (const char *) opt->data + offset : NULL;
282 /* Stores in '*out' the boolean value at byte offset 'offset' in the DHCP
283 * option in 'msg' represented by 'code' (which must be in the range
284 * 0...DHCP_N_OPTIONS). Returns true if successful, false if the option has
285 * fewer than 'offset + 1' bytes. */
287 dhcp_msg_get_bool(const struct dhcp_msg *msg, int code, size_t offset,
290 const uint8_t *uint8 = dhcp_msg_get(msg, code, offset, sizeof *uint8);
299 /* Stores in '*out' the 32-bit count of seconds at offset 'offset' (in
300 * 4-byte increments) in the DHCP option in 'msg' represented by 'code'
301 * (which must be in the range 0...DHCP_N_OPTIONS). The value is converted to
302 * native byte order. Returns true if successful, false if the option has
303 * fewer than '4 * (offset + 1)' bytes. */
305 dhcp_msg_get_secs(const struct dhcp_msg *msg, int code, size_t offset,
308 const uint32_t *uint32 = dhcp_msg_get(msg, code, offset * sizeof *uint32,
311 *out = ntohl(*uint32);
318 /* Stores in '*out' the IP address at offset 'offset' (in 4-byte increments) in
319 * the DHCP option in 'msg' represented by 'code' (which must be in the range
320 * 0...DHCP_N_OPTIONS). The IP address is stored in network byte order.
321 * Returns true if successful, false if the option has fewer than '4 * (offset
324 dhcp_msg_get_ip(const struct dhcp_msg *msg, int code,
325 size_t offset, uint32_t *out)
327 const uint32_t *uint32 = dhcp_msg_get(msg, code, offset * sizeof *uint32,
337 /* Returns the string in the DHCP option in 'msg' represented by 'code' (which
338 * must be in the range 0...DHCP_N_OPTIONS). The caller is responsible for
339 * freeing the string with free().
341 * If 'msg' has no option represented by 'code', returns a null pointer. (If
342 * the option was specified but had no content, then an empty string is
343 * returned, not a null pointer.) */
345 dhcp_msg_get_string(const struct dhcp_msg *msg, int code)
347 const struct dhcp_option *opt = &msg->options[code];
348 return opt->data ? xmemdup0(opt->data, opt->n) : NULL;
351 /* Stores in '*out' the octet at byte offset 'offset' in the DHCP option in
352 * 'msg' represented by 'code' (which must be in the range 0...DHCP_N_OPTIONS).
353 * Returns true if successful, false if the option has fewer than 'offset + 1'
356 dhcp_msg_get_uint8(const struct dhcp_msg *msg, int code,
357 size_t offset, uint8_t *out)
359 const uint8_t *uint8 = dhcp_msg_get(msg, code, offset, sizeof *uint8);
368 /* Stores in '*out' the 16-bit value at offset 'offset' (in 2-byte units) in
369 * the DHCP option in 'msg' represented by 'code' (which must be in the range
370 * 0...DHCP_N_OPTIONS). The value is converted to native byte order. Returns
371 * true if successful, false if the option has fewer than '2 * (offset + 1)'
374 dhcp_msg_get_uint16(const struct dhcp_msg *msg, int code,
375 size_t offset, uint16_t *out)
377 const uint16_t *uint16 = dhcp_msg_get(msg, code, offset * sizeof *uint16,
380 *out = ntohs(*uint16);
387 /* Appends a string representing 'duration' seconds to 'ds'. */
389 put_duration(struct ds *ds, unsigned int duration)
392 if (duration >= 86400) {
393 ds_put_format(ds, "%ud", duration / 86400);
396 if (duration >= 3600) {
397 ds_put_format(ds, "%uh", duration / 3600);
400 if (duration >= 60) {
401 ds_put_format(ds, "%umin", duration / 60);
405 ds_put_format(ds, "%us", duration);
408 ds_put_cstr(ds, "0s");
412 /* Appends a string representation of 'opt', which has the given 'code', to
415 dhcp_option_to_string(const struct dhcp_option *opt, int code, struct ds *ds)
417 const struct option_class *class = get_option_class(code);
418 const struct arg_type *type = &types[class->type];
422 for (cp = class->name; *cp; cp++) {
423 unsigned char c = *cp;
424 ds_put_char(ds, c == '_' ? '-' : tolower(c));
426 ds_put_char(ds, '=');
428 if (!opt->data || !opt->n) {
429 ds_put_cstr(ds, opt->data ? "empty" : "null");
433 if (class->type == DHCP_ARG_STRING) {
434 ds_put_char(ds, '"');
435 ds_put_printable(ds, opt->data, opt->n);
436 ds_put_char(ds, '"');
439 for (offset = 0; offset + type->size <= opt->n; offset += type->size) {
440 const void *p = (const char *) opt->data + offset;
441 const uint8_t *uint8 = p;
442 const uint32_t *uint32 = p;
443 const uint16_t *uint16 = p;
445 if (offset && class->type != DHCP_ARG_STRING) {
446 ds_put_cstr(ds, class->type == DHCP_ARG_UINT8 ? ":" : ", ");
448 switch (class->type) {
452 ds_put_format(ds, IP_FMT, IP_ARGS(uint32));
455 ds_put_format(ds, "%02"PRIx8, *uint8);
457 case DHCP_ARG_UINT16:
458 ds_put_format(ds, "%"PRIu16, ntohs(*uint16));
460 case DHCP_ARG_UINT32:
461 ds_put_format(ds, "%"PRIu32, ntohl(*uint32));
464 put_duration(ds, ntohl(*uint32));
466 case DHCP_ARG_STRING:
468 case DHCP_ARG_BOOLEAN:
470 ds_put_cstr(ds, "false");
471 } else if (*uint8 == 1) {
472 ds_put_cstr(ds, "true");
474 ds_put_format(ds, "**%"PRIu8"**", *uint8);
479 if (offset != opt->n) {
481 ds_put_cstr(ds, ", ");
483 ds_put_cstr(ds, "**leftovers:");
484 for (; offset < opt->n; offset++) {
485 const void *p = (const char *) opt->data + offset;
486 const uint8_t *uint8 = p;
487 ds_put_format(ds, " %"PRIu8, *uint8);
489 ds_put_cstr(ds, "**");
494 /* Returns true if 'a' and 'b' have the same content, false otherwise. */
496 dhcp_option_equals(const struct dhcp_option *a, const struct dhcp_option *b)
498 return ((a->data != NULL) == (b->data != NULL)
500 && !memcmp(a->data, b->data, a->n));
503 /* Replaces 'ds' by a string representation of 'msg'. If 'multiline' is
504 * false, 'ds' receives a single-line representation of 'msg', otherwise a
505 * multiline representation. */
507 dhcp_msg_to_string(const struct dhcp_msg *msg, bool multiline, struct ds *ds)
509 char separator = multiline ? '\n' : ' ';
513 ds_put_format(ds, "op=%s",
514 (msg->op == DHCP_BOOTREQUEST ? "request"
515 : msg->op == DHCP_BOOTREPLY ? "reply"
517 ds_put_format(ds, "%ctype=%s", separator, dhcp_type_name(msg->type));
518 ds_put_format(ds, "%cxid=0x%08"PRIx32, separator, msg->xid);
519 ds_put_format(ds, "%csecs=", separator);
520 put_duration(ds, msg->secs);
522 ds_put_format(ds, "%cflags=", separator);
523 if (msg->flags & DHCP_FLAGS_BROADCAST) {
524 ds_put_cstr(ds, "[BROADCAST]");
526 if (msg->flags & DHCP_FLAGS_MBZ) {
527 ds_put_format(ds, "[0x%04"PRIx16"]", msg->flags & DHCP_FLAGS_MBZ);
531 ds_put_format(ds, "%cciaddr="IP_FMT, separator, IP_ARGS(&msg->ciaddr));
534 ds_put_format(ds, "%cyiaddr="IP_FMT, separator, IP_ARGS(&msg->yiaddr));
537 ds_put_format(ds, "%csiaddr="IP_FMT, separator, IP_ARGS(&msg->siaddr));
540 ds_put_format(ds, "%cgiaddr="IP_FMT, separator, IP_ARGS(&msg->giaddr));
542 ds_put_format(ds, "%cchaddr="ETH_ADDR_FMT,
543 separator, ETH_ADDR_ARGS(msg->chaddr));
545 for (code = 0; code < DHCP_N_OPTIONS; code++) {
546 const struct dhcp_option *opt = &msg->options[code];
548 ds_put_char(ds, separator);
549 dhcp_option_to_string(opt, code, ds);
553 ds_put_char(ds, separator);
559 parse_options(struct dhcp_msg *msg, const char *name, void *data, size_t size,
570 code = ofpbuf_try_pull(&b, 1);
571 if (!code || *code == DHCP_CODE_END) {
573 } else if (*code == DHCP_CODE_PAD) {
577 len = ofpbuf_try_pull(&b, 1);
579 VLOG_DBG_RL(&rl, "reached end of %s expecting length byte", name);
583 payload = ofpbuf_try_pull(&b, *len);
585 VLOG_DBG_RL(&rl, "expected %"PRIu8" bytes of option-%"PRIu8" "
586 "payload with only %zu bytes of %s left",
587 *len, *code, b.size, name);
590 dhcp_msg_put(msg, *code + option_offset, payload, *len);
595 validate_options(struct dhcp_msg *msg)
599 for (code = 0; code < DHCP_N_OPTIONS; code++) {
600 struct dhcp_option *opt = &msg->options[code];
601 const struct option_class *class = get_option_class(code);
602 struct arg_type *type = &types[class->type];
604 size_t n_elems = opt->n / type->size;
605 size_t remainder = opt->n % type->size;
608 VLOG_DBG_RL(&rl, "%s option has %zu %zu-byte %s arguments "
609 "with %zu bytes left over",
610 class->name, n_elems, type->size,
611 type->name, remainder);
614 if (n_elems < class->min_args || n_elems > class->max_args) {
615 VLOG_DBG_RL(&rl, "%s option has %zu %zu-byte %s arguments but "
616 "between %zu and %zu are required",
617 class->name, n_elems, type->size, type->name,
618 class->min_args, class->max_args);
622 struct ds ds = DS_EMPTY_INITIALIZER;
623 VLOG_DBG_RL(&rl, "%s option contains: %s", class->name,
624 dhcp_option_to_string(opt, code, &ds));
634 /* Attempts to parse 'b' as a DHCP message. If successful, initializes '*msg'
635 * to the parsed message and returns 0. Otherwise, returns a positive errno
636 * value and '*msg' is indeterminate. */
638 dhcp_parse(struct dhcp_msg *msg, const struct ofpbuf *b_)
640 struct ofpbuf b = *b_;
641 struct dhcp_header *dhcp;
646 dhcp = ofpbuf_try_pull(&b, sizeof *dhcp);
648 VLOG_DBG_RL(&rl, "buffer too small for DHCP header (%zu bytes)",
653 if (dhcp->op != DHCP_BOOTREPLY && dhcp->op != DHCP_BOOTREQUEST) {
654 VLOG_DBG_RL(&rl, "invalid DHCP op (%"PRIu8")", dhcp->op);
657 if (dhcp->htype != ARP_HRD_ETHERNET) {
658 VLOG_DBG_RL(&rl, "invalid DHCP htype (%"PRIu8")", dhcp->htype);
661 if (dhcp->hlen != ETH_ADDR_LEN) {
662 VLOG_DBG_RL(&rl, "invalid DHCP hlen (%"PRIu8")", dhcp->hlen);
668 msg->xid = ntohl(dhcp->xid);
669 msg->secs = ntohs(dhcp->secs);
670 msg->flags = ntohs(dhcp->flags);
671 msg->ciaddr = dhcp->ciaddr;
672 msg->yiaddr = dhcp->yiaddr;
673 msg->siaddr = dhcp->siaddr;
674 msg->giaddr = dhcp->giaddr;
675 memcpy(msg->chaddr, dhcp->chaddr, ETH_ADDR_LEN);
677 cookie = ofpbuf_try_pull(&b, sizeof cookie);
679 if (ntohl(*cookie) == DHCP_OPTS_COOKIE) {
682 parse_options(msg, "options", b.data, b.size, 0);
683 if (dhcp_msg_get_uint8(msg, DHCP_CODE_OPTION_OVERLOAD,
686 parse_options(msg, "file", dhcp->file, sizeof dhcp->file,
690 parse_options(msg, "sname",
691 dhcp->sname, sizeof dhcp->sname, 0);
695 VLOG_DBG_RL(&rl, "bad DHCP options cookie: %08"PRIx32,
699 VLOG_DBG_RL(&rl, "DHCP packet has no options");
702 vendor_class = dhcp_msg_get_string(msg, DHCP_CODE_VENDOR_CLASS);
703 if (vendor_class && !strcmp(vendor_class, "OpenFlow")) {
704 parse_options(msg, "vendor-specific",
705 msg->options[DHCP_CODE_VENDOR_SPECIFIC].data,
706 msg->options[DHCP_CODE_VENDOR_SPECIFIC].n,
711 validate_options(msg);
712 if (!dhcp_msg_get_uint8(msg, DHCP_CODE_DHCP_MSG_TYPE, 0, &type)) {
713 VLOG_DBG_RL(&rl, "missing DHCP message type");
714 dhcp_msg_uninit(msg);
721 if (VLOG_IS_DBG_ENABLED()) {
725 ds_put_hex_dump(&ds, b_->data, b_->size, 0, true);
726 VLOG_DBG_RL(&rl, "invalid DHCP message dump:\n%s", ds_cstr(&ds));
729 dhcp_msg_to_string(msg, false, &ds);
730 VLOG_DBG_RL(&rl, "partially dissected DHCP message: %s", ds_cstr(&ds));
738 put_option_chunk(struct ofpbuf *b, uint8_t code, void *data, size_t n)
745 ofpbuf_put(b, header, sizeof header);
746 ofpbuf_put(b, data, n);
750 put_option(struct ofpbuf *b, uint8_t code, void *data, size_t n)
754 /* Divide the data into chunks of 255 bytes or less. Make
755 * intermediate chunks multiples of 8 bytes in case the
756 * recipient validates a chunk at a time instead of the
757 * concatenated value. */
760 size_t chunk = n > 255 ? 248 : n;
761 put_option_chunk(b, code, p, chunk);
766 /* Option should be present but carry no data. */
767 put_option_chunk(b, code, NULL, 0);
772 /* Appends to 'b' the DHCP message represented by 'msg'. */
774 dhcp_assemble(const struct dhcp_msg *msg, struct ofpbuf *b)
776 const uint8_t end = DHCP_CODE_END;
777 uint32_t cookie = htonl(DHCP_OPTS_COOKIE);
778 struct ofpbuf vnd_data;
779 struct dhcp_header dhcp;
782 memset(&dhcp, 0, sizeof dhcp);
784 dhcp.htype = ARP_HRD_ETHERNET;
785 dhcp.hlen = ETH_ADDR_LEN;
787 dhcp.xid = htonl(msg->xid);
788 dhcp.secs = htons(msg->secs);
789 dhcp.flags = htons(msg->flags);
790 dhcp.ciaddr = msg->ciaddr;
791 dhcp.yiaddr = msg->yiaddr;
792 dhcp.siaddr = msg->siaddr;
793 dhcp.giaddr = msg->giaddr;
794 memcpy(dhcp.chaddr, msg->chaddr, ETH_ADDR_LEN);
795 ofpbuf_put(b, &dhcp, sizeof dhcp);
796 ofpbuf_put(b, &cookie, sizeof cookie);
798 /* Put DHCP message type first. (The ordering is not required but it
801 uint8_t type = msg->type;
802 put_option(b, DHCP_CODE_DHCP_MSG_TYPE, &type, 1);
805 /* Put the standard options. */
806 for (i = 0; i < DHCP_VENDOR_OFS; i++) {
807 const struct dhcp_option *option = &msg->options[i];
808 put_option(b, i, option->data, option->n);
811 /* Assemble vendor specific option and put it. */
812 ofpbuf_init(&vnd_data, 0);
813 for (i = DHCP_VENDOR_OFS; i < DHCP_N_OPTIONS; i++) {
814 const struct dhcp_option *option = &msg->options[i];
815 put_option(&vnd_data, i - DHCP_VENDOR_OFS, option->data, option->n);
818 put_option(b, DHCP_CODE_VENDOR_SPECIFIC, vnd_data.data, vnd_data.size);
820 ofpbuf_uninit(&vnd_data);
822 /* Put end-of-options option. */
823 ofpbuf_put(b, &end, sizeof end);