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