851c5638f1c06a372e5d27af8bc5bce1afbbba3d
[openvswitch] / lib / dhcp.c
1 /* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
2  * Junior University
3  *
4  * We are making the OpenFlow specification and associated documentation
5  * (Software) available for public use and benefit with the expectation
6  * that others will use, modify and enhance the Software and contribute
7  * those enhancements back to the community. However, since we would
8  * like to make the Software available for broadest use, with as few
9  * restrictions as possible permission is hereby granted, free of
10  * charge, to any person obtaining a copy of this Software to deal in
11  * the Software under the copyrights without restriction, including
12  * without limitation the rights to use, copy, modify, merge, publish,
13  * distribute, sublicense, and/or sell copies of the Software, and to
14  * permit persons to whom the Software is furnished to do so, subject to
15  * the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be
18  * included in all copies or substantial portions of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23  * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
24  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
25  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
26  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27  * SOFTWARE.
28  *
29  * The name and trademarks of copyright holder(s) may NOT be used in
30  * advertising or publicity pertaining to the Software or any
31  * derivatives without specific, written prior permission.
32  */
33
34 #include "dhcp.h"
35 #include <arpa/inet.h>
36 #include <assert.h>
37 #include <ctype.h>
38 #include <errno.h>
39 #include <inttypes.h>
40 #include <stdlib.h>
41 #include "buffer.h"
42 #include "dynamic-string.h"
43
44 #define THIS_MODULE VLM_dhcp
45 #include "vlog.h"
46
47 /* Information about a DHCP argument type. */
48 struct arg_type {
49     const char *name;           /* Name. */
50     size_t size;                /* Number of bytes per argument. */
51 };
52
53 static struct arg_type types[] = {
54 #define DHCP_ARG(NAME, SIZE) [DHCP_ARG_##NAME] = {#NAME, SIZE},
55     DHCP_ARGS
56 #undef DHCP_ARG
57 };
58
59 /* Information about a DHCP option. */
60 struct option_class {
61     const char *name;           /* Name. */
62     enum dhcp_arg_type type;    /* Argument type. */
63     size_t min_args;            /* Minimum number of arguments. */
64     size_t max_args;            /* Maximum number of arguments. */
65 };
66
67 static struct option_class classes[DHCP_N_OPTIONS] = {
68     [0 ... 255] = {NULL, DHCP_ARG_UINT8, 0, SIZE_MAX},
69 #define DHCP_OPT(NAME, CODE, TYPE, MIN, MAX) \
70     [CODE] = {#NAME, DHCP_ARG_##TYPE, MIN, MAX},
71     DHCP_OPTS
72 #undef DHCP_OPT
73 };
74
75 static void copy_data(struct dhcp_msg *);
76
77 const char *
78 dhcp_type_name(enum dhcp_msg_type type)
79 {
80     switch (type) {
81 #define DHCP_MSG(NAME, VALUE) case NAME: return #NAME;
82         DHCP_MSGS
83 #undef DHCP_MSG
84     }
85     return "<<unknown DHCP message type>>";
86 }
87
88 /* Initializes 'msg' as a DHCP message.  The message should be freed with
89  * dhcp_msg_uninit() when it is no longer needed. */
90 void
91 dhcp_msg_init(struct dhcp_msg *msg)
92 {
93     memset(msg, 0, sizeof *msg);
94 }
95
96 /* Frees the contents of 'msg'.  The caller is responsible for freeing 'msg',
97  * if necessary. */
98 void
99 dhcp_msg_uninit(struct dhcp_msg *msg)
100 {
101     if (msg) {
102         free(msg->data);
103     }
104 }
105
106 /* Initializes 'dst' as a copy of 'src'.  'dst' (and 'src') should be freed
107  * with dhcp_msg_uninit() when it is no longer needed. */
108 void
109 dhcp_msg_copy(struct dhcp_msg *dst, const struct dhcp_msg *src)
110 {
111     *dst = *src;
112     dst->data_allocated = src->data_used;
113     dst->data_used = 0;
114     dst->data = xmalloc(dst->data_allocated);
115     copy_data(dst);
116 }
117
118 static void
119 prealloc_data(struct dhcp_msg *msg, size_t n)
120 {
121     size_t needed = msg->data_used + n;
122     if (needed > msg->data_allocated) {
123         uint8_t *old_data = msg->data;
124         msg->data_allocated = MAX(needed * 2, 64);
125         msg->data = xmalloc(msg->data_allocated);
126         if (old_data) {
127             copy_data(msg);
128             free(old_data);
129         }
130     }
131 }
132
133 static void *
134 append_data(struct dhcp_msg *msg, const void *data, size_t n)
135 {
136     uint8_t *p = &msg->data[msg->data_used];
137     memcpy(p, data, n);
138     msg->data_used += n;
139     return p;
140 }
141
142 static void
143 copy_data(struct dhcp_msg *msg)
144 {
145     int code;
146
147     msg->data_used = 0;
148     for (code = 0; code < DHCP_N_OPTIONS; code++) {
149         struct dhcp_option *opt = &msg->options[code];
150         if (opt->data) {
151             assert(msg->data_used + opt->n <= msg->data_allocated);
152             opt->data = append_data(msg, opt->data, opt->n);
153         }
154     }
155 }
156
157 /* Appends the 'n' bytes in 'data' to the DHCP option in 'msg' represented by
158  * 'code' (which must be in the range 0...DHCP_N_OPTIONS). */
159 void
160 dhcp_msg_put(struct dhcp_msg *msg, int code,
161              const void *data, size_t n)
162 {
163     struct dhcp_option *opt;
164     if (code == DHCP_CODE_PAD || code == DHCP_CODE_END) {
165         return;
166     }
167
168     opt = &msg->options[code];
169     prealloc_data(msg, n + opt->n);
170     if (opt->n) {
171         if (&msg->data[msg->data_used - opt->n] != opt->data) {
172             opt->data = append_data(msg, opt->data, opt->n);
173         }
174         append_data(msg, data, n);
175     } else {
176         opt->data = append_data(msg, data, n);
177     }
178     opt->n += n;
179 }
180
181 /* Appends the boolean value 'b', as a octet with value 0 (false) or 1 (true),
182  * to the DHCP option in 'msg' represented by 'code' (which must be in the
183  * range 0...DHCP_N_OPTIONS). */
184 void
185 dhcp_msg_put_bool(struct dhcp_msg *msg, int code, bool b_)
186 {
187     char b = !!b_;
188     dhcp_msg_put(msg, code, &b, 1);
189 }
190
191 /* Appends the number of seconds 'secs', as a 32-bit number in network byte
192  * order, to the DHCP option in 'msg' represented by 'code' (which must be in
193  * the range 0...DHCP_N_OPTIONS). */
194 void
195 dhcp_msg_put_secs(struct dhcp_msg *msg, int code, uint32_t secs_)
196 {
197     uint32_t secs = htonl(secs_);
198     dhcp_msg_put(msg, code, &secs, sizeof secs);
199 }
200
201 /* Appends the IP address 'ip', as a 32-bit number in network byte order, to
202  * the DHCP option in 'msg' represented by 'code' (which must be in the range
203  * 0...DHCP_N_OPTIONS). */
204 void
205 dhcp_msg_put_ip(struct dhcp_msg *msg, int code, uint32_t ip)
206 {
207     dhcp_msg_put(msg, code, &ip, sizeof ip);
208 }
209
210 /* Appends the ASCII string 'string', to the DHCP option in 'msg' represented
211  * by 'code' (which must be in the range 0...DHCP_N_OPTIONS). */
212 void
213 dhcp_msg_put_string(struct dhcp_msg *msg, int code, const char *string)
214 {
215     dhcp_msg_put(msg, code, string, strlen(string));
216 }
217
218 /* Appends octet 'x' to DHCP option in 'msg' represented by 'code' (which must
219  * be in the range 0...DHCP_N_OPTIONS). */
220 void
221 dhcp_msg_put_uint8(struct dhcp_msg *msg, int code, uint8_t x)
222 {
223     dhcp_msg_put(msg, code, &x, sizeof x);
224 }
225
226 /* Appends the 'n' octets in 'data' to DHCP option in 'msg' represented by
227  * 'code' (which must be in the range 0...DHCP_N_OPTIONS). */
228 void dhcp_msg_put_uint8_array(struct dhcp_msg *msg, int code,
229                               const uint8_t data[], size_t n)
230 {
231     dhcp_msg_put(msg, code, data, n);
232 }
233
234 /* Appends the 16-bit value in 'x', in network byte order, to DHCP option in
235  * 'msg' represented by 'code' (which must be in the range
236  * 0...DHCP_N_OPTIONS). */
237 void
238 dhcp_msg_put_uint16(struct dhcp_msg *msg, int code, uint16_t x_)
239 {
240     uint16_t x = htons(x_);
241     dhcp_msg_put(msg, code, &x, sizeof x);
242 }
243
244
245 /* Appends the 'n' 16-bit values in 'data', in network byte order, to DHCP
246  * option in 'msg' represented by 'code' (which must be in the range
247  * 0...DHCP_N_OPTIONS). */
248 void
249 dhcp_msg_put_uint16_array(struct dhcp_msg *msg, int code,
250                           const uint16_t data[], size_t n)
251 {
252     size_t i;
253
254     for (i = 0; i < n; i++) {
255         dhcp_msg_put_uint16(msg, code, data[i]);
256     }
257 }
258
259 /* Returns a pointer to the 'size' bytes starting at byte offset 'offset' in
260  * the DHCP option in 'msg' represented by 'code' (which must be in the range
261  * 0...DHCP_N_OPTIONS).  If the option has fewer than 'offset + size' bytes,
262  * returns a null pointer. */
263 const void *
264 dhcp_msg_get(const struct dhcp_msg *msg, int code,
265              size_t offset, size_t size)
266 {
267     const struct dhcp_option *opt = &msg->options[code];
268     return offset + size <= opt->n ? (const char *) opt->data + offset : NULL;
269 }
270
271 /* Stores in '*out' the boolean value at byte offset 'offset' in the DHCP
272  * option in 'msg' represented by 'code' (which must be in the range
273  * 0...DHCP_N_OPTIONS).  Returns true if successful, false if the option has
274  * fewer than 'offset + 1' bytes. */
275 bool
276 dhcp_msg_get_bool(const struct dhcp_msg *msg, int code, size_t offset,
277                   bool *out)
278 {
279     const uint8_t *uint8 = dhcp_msg_get(msg, code, offset, sizeof *uint8);
280     if (uint8) {
281         *out = *uint8 != 0;
282         return true;
283     } else {
284         return false;
285     }
286 }
287
288 /* Stores in '*out' the 32-bit count of seconds at offset 'offset' (in
289  * 4-byte increments) in the DHCP option in 'msg' represented by 'code'
290  * (which must be in the range 0...DHCP_N_OPTIONS).  The value is converted to
291  * native byte order.  Returns true if successful, false if the option has
292  * fewer than '4 * (offset + 1)' bytes. */
293 bool
294 dhcp_msg_get_secs(const struct dhcp_msg *msg, int code, size_t offset,
295                   uint32_t *out)
296 {
297     const uint32_t *uint32 = dhcp_msg_get(msg, code, offset * sizeof *uint32,
298                                           sizeof *uint32);
299     if (uint32) {
300         *out = ntohl(*uint32);
301         return true;
302     } else {
303         return false;
304     }
305 }
306
307 /* Stores in '*out' the IP address at offset 'offset' (in 4-byte increments) in
308  * the DHCP option in 'msg' represented by 'code' (which must be in the range
309  * 0...DHCP_N_OPTIONS).  The IP address is stored in network byte order.
310  * Returns true if successful, false if the option has fewer than '4 * (offset
311  * + 1)' bytes. */
312 bool
313 dhcp_msg_get_ip(const struct dhcp_msg *msg, int code,
314                 size_t offset, uint32_t *out)
315 {
316     const uint32_t *uint32 = dhcp_msg_get(msg, code, offset * sizeof *uint32,
317                                           sizeof *uint32);
318     if (uint32) {
319         *out = *uint32;
320         return true;
321     } else {
322         return false;
323     }
324 }
325
326 /* Returns the string in the DHCP option in 'msg' represented by 'code' (which
327  * must be in the range 0...DHCP_N_OPTIONS).  The caller is responsible for
328  * freeing the string with free().
329  *
330  * If 'msg' has no option represented by 'code', returns a null pointer.  (If
331  * the option was specified but had no content, then an empty string is
332  * returned, not a null pointer.) */
333 char *
334 dhcp_msg_get_string(const struct dhcp_msg *msg, int code)
335 {
336     const struct dhcp_option *opt = &msg->options[code];
337     return opt->data ? xmemdup0(opt->data, opt->n) : NULL;
338 }
339
340 /* Stores in '*out' the octet at byte offset 'offset' in the DHCP option in
341  * 'msg' represented by 'code' (which must be in the range 0...DHCP_N_OPTIONS).
342  * Returns true if successful, false if the option has fewer than 'offset + 1'
343  * bytes. */
344 bool
345 dhcp_msg_get_uint8(const struct dhcp_msg *msg, int code,
346                    size_t offset, uint8_t *out)
347 {
348     const uint8_t *uint8 = dhcp_msg_get(msg, code, offset, sizeof *uint8);
349     if (uint8) {
350         *out = *uint8;
351         return true;
352     } else {
353         return false;
354     }
355 }
356
357 /* Stores in '*out' the 16-bit value at offset 'offset' (in 2-byte units) in
358  * the DHCP option in 'msg' represented by 'code' (which must be in the range
359  * 0...DHCP_N_OPTIONS).  The value is converted to native byte order.  Returns
360  * true if successful, false if the option has fewer than '2 * (offset + 1)'
361  * bytes. */
362 bool
363 dhcp_msg_get_uint16(const struct dhcp_msg *msg, int code,
364                     size_t offset, uint16_t *out)
365 {
366     const uint16_t *uint16 = dhcp_msg_get(msg, code, offset * sizeof *uint16,
367                                           sizeof *uint16);
368     if (uint16) {
369         *out = ntohs(*uint16);
370         return true;
371     } else {
372         return false;
373     }
374 }
375
376 /* Appends a string representation of 'opt', which has the given 'code', to
377  * 'ds'. */
378 const char *
379 dhcp_option_to_string(const struct dhcp_option *opt, int code, struct ds *ds)
380 {
381     struct option_class *class = &classes[code];
382     const struct arg_type *type = &types[class->type];
383     size_t offset;
384
385     ds_put_char(ds, ' ');
386     if (class->name) {
387         const char *cp;
388         for (cp = class->name; *cp; cp++) {
389             unsigned char c = *cp;
390             ds_put_char(ds, c == '_' ? '-' : tolower(c));
391         }
392     } else {
393         ds_put_format(ds, "option-%d", code);
394     }
395     ds_put_char(ds, '=');
396
397     if (class->type == DHCP_ARG_STRING) {
398         ds_put_char(ds, '"');
399     }
400     for (offset = 0; offset + type->size <= opt->n; offset += type->size) {
401         const void *p = (const char *) opt->data + offset;
402         const uint8_t *uint8 = p;
403         const uint32_t *uint32 = p;
404         const uint16_t *uint16 = p;
405         const char *cp = p;
406         unsigned char c;
407         unsigned int secs;
408
409         if (offset && class->type != DHCP_ARG_STRING) {
410             ds_put_cstr(ds, class->type == DHCP_ARG_UINT8 ? ":" : ", ");
411         }
412         switch (class->type) {
413         case DHCP_ARG_FIXED:
414             NOT_REACHED();
415         case DHCP_ARG_IP:
416             ds_put_format(ds, IP_FMT, IP_ARGS(uint32));
417             break;
418         case DHCP_ARG_UINT8:
419             ds_put_format(ds, "%02"PRIx8, *uint8);
420             break;
421         case DHCP_ARG_UINT16:
422             ds_put_format(ds, "%"PRIu16, ntohs(*uint16));
423             break;
424         case DHCP_ARG_UINT32:
425             ds_put_format(ds, "%"PRIu32, ntohl(*uint32));
426             break;
427         case DHCP_ARG_SECS:
428             secs = ntohl(*uint32);
429             if (secs >= 86400) {
430                 ds_put_format(ds, "%ud", secs / 86400);
431                 secs %= 86400;
432             }
433             if (secs >= 3600) {
434                 ds_put_format(ds, "%uh", secs / 3600);
435                 secs %= 3600;
436             }
437             if (secs >= 60) {
438                 ds_put_format(ds, "%umin", secs / 60);
439                 secs %= 60;
440             }
441             if (secs > 0 || *uint32 == 0) {
442                 ds_put_format(ds, "%us", secs);
443             }
444             break;
445         case DHCP_ARG_STRING:
446             c = *cp;
447             if (isprint(c) && (!isspace(c) || c == ' ') && c != '\\') {
448                 ds_put_char(ds, *cp);
449             } else {
450                 ds_put_format(ds, "\\%03o", (int) c);
451             }
452             break;
453         case DHCP_ARG_BOOLEAN:
454             if (*uint8 == 0) {
455                 ds_put_cstr(ds, "false");
456             } else if (*uint8 == 1) {
457                 ds_put_cstr(ds, "true");
458             } else {
459                 ds_put_format(ds, "**%"PRIu8"**", *uint8);
460             }
461             break;
462         }
463     }
464     if (class->type == DHCP_ARG_STRING) {
465         ds_put_char(ds, '"');
466     }
467     if (offset != opt->n) {
468         if (offset) {
469             ds_put_cstr(ds, ", ");
470         }
471         ds_put_cstr(ds, "**leftovers:");
472         for (; offset < opt->n; offset++) {
473             const void *p = (const char *) opt->data + offset;
474             const uint8_t *uint8 = p;
475             ds_put_format(ds, " %"PRIu8, *uint8);
476         }
477         ds_put_cstr(ds, "**");
478     }
479     return ds_cstr(ds);
480 }
481
482 /* Replaces 'ds' by a string representation of 'msg'. */
483 const char *
484 dhcp_msg_to_string(const struct dhcp_msg *msg, struct ds *ds)
485 {
486     int code;
487
488     ds_clear(ds);
489     ds_put_format(ds, "%s %s xid=%08"PRIx32" secs=%"PRIu16,
490                   (msg->op == DHCP_BOOTREQUEST ? "BOOTREQUEST"
491                    : msg->op == DHCP_BOOTREPLY ? "BOOTREPLY"
492                    : "<<bad DHCP op>>"),
493                   dhcp_type_name(msg->type), msg->xid, msg->secs);
494     if (msg->flags) {
495         ds_put_cstr(ds, " flags=");
496         if (msg->flags & DHCP_FLAGS_BROADCAST) {
497             ds_put_cstr(ds, "[BROADCAST]");
498         }
499         if (msg->flags & DHCP_FLAGS_MBZ) {
500             ds_put_format(ds, "[0x%04"PRIx16"]", msg->flags & DHCP_FLAGS_MBZ);
501         }
502     }
503     if (msg->ciaddr) {
504         ds_put_format(ds, " ciaddr="IP_FMT, IP_ARGS(&msg->ciaddr));
505     }
506     if (msg->yiaddr) {
507         ds_put_format(ds, " yiaddr="IP_FMT, IP_ARGS(&msg->yiaddr));
508     }
509     if (msg->siaddr) {
510         ds_put_format(ds, " siaddr="IP_FMT, IP_ARGS(&msg->siaddr));
511     }
512     if (msg->giaddr) {
513         ds_put_format(ds, " giaddr="IP_FMT, IP_ARGS(&msg->giaddr));
514     }
515     ds_put_format(ds, " chaddr="ETH_ADDR_FMT, ETH_ADDR_ARGS(msg->chaddr));
516
517     for (code = 0; code < DHCP_N_OPTIONS; code++) {
518         const struct dhcp_option *opt = &msg->options[code];
519         if (opt->data) {
520             dhcp_option_to_string(opt, code, ds);
521         }
522     }
523     return ds_cstr(ds);
524 }
525
526 static void
527 parse_options(struct dhcp_msg *msg, const char *name, void *data, size_t size,
528               int option_offset)
529 {
530     struct buffer b;
531
532     b.data = data;
533     b.size = size;
534     for (;;) {
535         uint8_t *code, *len;
536         void *payload;
537
538         code = buffer_try_pull(&b, 1);
539         if (!code || *code == DHCP_CODE_END) {
540             break;
541         } else if (*code == DHCP_CODE_PAD) {
542             continue;
543         }
544
545         len = buffer_try_pull(&b, 1);
546         if (!len) {
547             VLOG_DBG("reached end of %s expecting length byte", name);
548             break;
549         }
550
551         payload = buffer_try_pull(&b, *len);
552         if (!payload) {
553             VLOG_DBG("expected %"PRIu8" bytes of option-%"PRIu8" payload "
554                      "with only %zu bytes of %s left",
555                      *len, *code, b.size, name);
556             break;
557         }
558         dhcp_msg_put(msg, *code + option_offset, payload, *len);
559     }
560 }
561
562 static void
563 validate_options(struct dhcp_msg *msg)
564 {
565     int code;
566
567     for (code = 0; code < DHCP_N_OPTIONS; code++) {
568         struct dhcp_option *opt = &msg->options[code];
569         struct option_class *class = &classes[code];
570         struct arg_type *type = &types[class->type];
571         if (opt->data) {
572             size_t n_elems = opt->n / type->size;
573             size_t remainder = opt->n % type->size;
574             bool ok = true;
575             if (remainder) {
576                 VLOG_DBG("%s option has %zu %zu-byte %s arguments with "
577                          "%zu bytes left over",
578                          class->name, n_elems, type->size,
579                          type->name, remainder);
580                 ok = false;
581             }
582             if (n_elems < class->min_args || n_elems > class->max_args) {
583                 VLOG_DBG("%s option has %zu %zu-byte %s arguments but "
584                          "between %zu and %zu are required",
585                          class->name, n_elems, type->size, type->name,
586                          class->min_args, class->max_args);
587                 ok = false;
588             }
589             if (!ok) {
590                 struct ds ds = DS_EMPTY_INITIALIZER;
591                 VLOG_DBG("%s option contains: %s",
592                          class->name, dhcp_option_to_string(opt, code, &ds));
593                 ds_destroy(&ds);
594
595                 opt->n = 0;
596                 opt->data = NULL;
597             }
598         }
599     }
600 }
601
602 /* Attempts to parse 'b' as a DHCP message.  If successful, initializes '*msg'
603  * to the parsed message and returns 0.  Otherwise, returns a positive errno
604  * value and '*msg' is indeterminate. */
605 int
606 dhcp_parse(struct dhcp_msg *msg, const struct buffer *b_)
607 {
608     struct buffer b = *b_;
609     struct dhcp_header *dhcp;
610     uint32_t *cookie;
611     uint8_t type;
612     char *vendor_class;
613
614     dhcp = buffer_try_pull(&b, sizeof *dhcp);
615     if (!dhcp) {
616         VLOG_DBG("buffer too small for DHCP header (%zu bytes)", b.size);
617         goto error;
618     }
619
620     if (dhcp->op != DHCP_BOOTREPLY && dhcp->op != DHCP_BOOTREQUEST) {
621         VLOG_DBG("invalid DHCP op (%"PRIu8")", dhcp->op);
622         goto error;
623     }
624     if (dhcp->htype != ARP_HRD_ETHERNET) {
625         VLOG_DBG("invalid DHCP htype (%"PRIu8")", dhcp->htype);
626         goto error;
627     }
628     if (dhcp->hlen != ETH_ADDR_LEN) {
629         VLOG_DBG("invalid DHCP hlen (%"PRIu8")", dhcp->hlen);
630         goto error;
631     }
632
633     dhcp_msg_init(msg);
634     msg->op = dhcp->op;
635     msg->xid = ntohl(dhcp->xid);
636     msg->secs = ntohs(dhcp->secs);
637     msg->flags = ntohs(dhcp->flags);
638     msg->ciaddr = dhcp->ciaddr;
639     msg->yiaddr = dhcp->yiaddr;
640     msg->siaddr = dhcp->siaddr;
641     msg->giaddr = dhcp->giaddr;
642     memcpy(msg->chaddr, dhcp->chaddr, ETH_ADDR_LEN);
643
644     cookie = buffer_try_pull(&b, sizeof cookie);
645     if (cookie) {
646         if (ntohl(*cookie) == DHCP_OPTS_COOKIE) {
647             uint8_t overload;
648
649             parse_options(msg, "options", b.data, b.size, 0);
650             if (dhcp_msg_get_uint8(msg, DHCP_CODE_OPTION_OVERLOAD,
651                                    0, &overload)) {
652                 if (overload & 1) {
653                     parse_options(msg, "file", dhcp->file, sizeof dhcp->file,
654                                   0);
655                 }
656                 if (overload & 2) {
657                     parse_options(msg, "sname",
658                                   dhcp->sname, sizeof dhcp->sname, 0);
659                 }
660             }
661         } else {
662             VLOG_DBG("bad DHCP options cookie: %08"PRIx32, ntohl(*cookie));
663         }
664     } else {
665         VLOG_DBG("DHCP packet has no options");
666     }
667
668     vendor_class = dhcp_msg_get_string(msg, DHCP_CODE_VENDOR_CLASS);
669     if (vendor_class && !strcmp(vendor_class, "OpenFlow")) {
670         parse_options(msg, "vendor-specific",
671                       msg->options[DHCP_CODE_VENDOR_SPECIFIC].data,
672                       msg->options[DHCP_CODE_VENDOR_SPECIFIC].n,
673                       DHCP_VENDOR_OFS);
674     }
675     free(vendor_class);
676
677     validate_options(msg);
678     if (!dhcp_msg_get_uint8(msg, DHCP_CODE_DHCP_MSG_TYPE, 0, &type)) {
679         VLOG_DBG("missing DHCP message type");
680         dhcp_msg_uninit(msg);
681         goto error;
682     }
683     msg->type = type;
684     return 0;
685
686 error:
687     if (VLOG_IS_DBG_ENABLED()) {
688         struct ds ds;
689
690         ds_init(&ds);
691         ds_put_hex_dump(&ds, b_->data, b_->size, 0, true);
692         VLOG_DBG("invalid DHCP message dump:\n%s", ds_cstr(&ds));
693
694         ds_clear(&ds);
695         dhcp_msg_to_string(msg, &ds);
696         VLOG_DBG("partially dissected DHCP message: %s", ds_cstr(&ds));
697
698         ds_destroy(&ds);
699     }
700     return EPROTO;
701 }
702
703 static void
704 put_option_chunk(struct buffer *b, uint8_t code, void *data, size_t n)
705 {
706     uint8_t header[2];
707
708     assert(n < 256);
709     header[0] = code;
710     header[1] = n;
711     buffer_put(b, header, sizeof header);
712     buffer_put(b, data, n);
713 }
714
715 static void
716 put_option(struct buffer *b, uint8_t code, void *data, size_t n)
717 {
718     if (data) {
719         if (n) {
720             /* Divide the data into chunks of 255 bytes or less.  Make
721              * intermediate chunks multiples of 8 bytes in case the
722              * recipient validates a chunk at a time instead of the
723              * concatenated value. */
724             uint8_t *p = data;
725             while (n) {
726                 size_t chunk = n > 255 ? 248 : n;
727                 put_option_chunk(b, code, p, chunk);
728                 p += chunk;
729                 n -= chunk;
730             }
731         } else {
732             /* Option should be present but carry no data. */
733             put_option_chunk(b, code, NULL, 0);
734         }
735     }
736 }
737
738 /* Appends to 'b' the DHCP message represented by 'msg'. */
739 void
740 dhcp_assemble(const struct dhcp_msg *msg, struct buffer *b)
741 {
742     const uint8_t end = DHCP_CODE_END;
743     uint32_t cookie = htonl(DHCP_OPTS_COOKIE);
744     struct buffer vnd_data;
745     struct dhcp_header dhcp;
746     int i;
747
748     memset(&dhcp, 0, sizeof dhcp);
749     dhcp.op = msg->op;
750     dhcp.htype = ARP_HRD_ETHERNET;
751     dhcp.hlen = ETH_ADDR_LEN;
752     dhcp.hops = 0;
753     dhcp.xid = htonl(msg->xid);
754     dhcp.secs = htons(msg->secs);
755     dhcp.flags = htons(msg->flags);
756     dhcp.ciaddr = msg->ciaddr;
757     dhcp.yiaddr = msg->yiaddr;
758     dhcp.siaddr = msg->siaddr;
759     dhcp.giaddr = msg->giaddr;
760     memcpy(dhcp.chaddr, msg->chaddr, ETH_ADDR_LEN);
761     buffer_put(b, &dhcp, sizeof dhcp);
762     buffer_put(b, &cookie, sizeof cookie);
763
764     /* Put DHCP message type first.  (The ordering is not required but it
765      * seems polite.) */
766     if (msg->type) {
767         uint8_t type = msg->type;
768         put_option(b, DHCP_CODE_DHCP_MSG_TYPE, &type, 1);
769     }
770
771     /* Put the standard options. */
772     for (i = 0; i < DHCP_VENDOR_OFS; i++) {
773         const struct dhcp_option *option = &msg->options[i];
774         put_option(b, i, option->data, option->n);
775     }
776
777     /* Assemble vendor specific option and put it. */
778     buffer_init(&vnd_data, 0);
779     for (i = DHCP_VENDOR_OFS; i < DHCP_N_OPTIONS; i++) {
780         const struct dhcp_option *option = &msg->options[i];
781         put_option(&vnd_data, i - DHCP_VENDOR_OFS, option->data, option->n);
782     }
783     if (vnd_data.size) {
784         put_option(b, DHCP_CODE_VENDOR_SPECIFIC, vnd_data.data, vnd_data.size);
785     }
786     buffer_uninit(&vnd_data);
787
788     /* Put end-of-options option. */
789     buffer_put(b, &end, sizeof end);
790 }
791