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