ovsdb: Add support for referential integrity in the database itself.
[openvswitch] / lib / dhcp.c
1 /*
2  * Copyright (c) 2008 Nicira Networks.
3  *
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:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 #include <config.h>
18 #include "dhcp.h"
19 #include <arpa/inet.h>
20 #include <assert.h>
21 #include <ctype.h>
22 #include <errno.h>
23 #include <inttypes.h>
24 #include <stdlib.h>
25 #include "dynamic-string.h"
26 #include "ofpbuf.h"
27
28 #define THIS_MODULE VLM_dhcp
29 #include "vlog.h"
30
31 /* Information about a DHCP argument type. */
32 struct arg_type {
33     const char *name;           /* Name. */
34     size_t size;                /* Number of bytes per argument. */
35 };
36
37 static struct arg_type types[] = {
38 #define DHCP_ARG(NAME, SIZE) [DHCP_ARG_##NAME] = {#NAME, SIZE},
39     DHCP_ARGS
40 #undef DHCP_ARG
41 };
42
43 /* Information about a DHCP option. */
44 struct option_class {
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. */
49 };
50
51 static const struct option_class *
52 get_option_class(int code)
53 {
54     static struct option_class classes[DHCP_N_OPTIONS];
55     static bool init = false;
56     if (!init) {
57         int i;
58
59         init = true;
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;
65         DHCP_OPTS
66 #undef DHCP_OPT
67
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;
74             }
75         }
76     }
77     assert(code >= 0 && code < DHCP_N_OPTIONS);
78     return &classes[code];
79 }
80
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);
85
86 static void copy_data(struct dhcp_msg *);
87
88 const char *
89 dhcp_type_name(enum dhcp_msg_type type)
90 {
91     switch (type) {
92 #define DHCP_MSG(NAME, VALUE) case NAME: return #NAME;
93         DHCP_MSGS
94 #undef DHCP_MSG
95     }
96     return "<<unknown DHCP message type>>";
97 }
98
99 /* Initializes 'msg' as a DHCP message.  The message should be freed with
100  * dhcp_msg_uninit() when it is no longer needed. */
101 void
102 dhcp_msg_init(struct dhcp_msg *msg)
103 {
104     memset(msg, 0, sizeof *msg);
105 }
106
107 /* Frees the contents of 'msg'.  The caller is responsible for freeing 'msg',
108  * if necessary. */
109 void
110 dhcp_msg_uninit(struct dhcp_msg *msg)
111 {
112     if (msg) {
113         free(msg->data);
114     }
115 }
116
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. */
119 void
120 dhcp_msg_copy(struct dhcp_msg *dst, const struct dhcp_msg *src)
121 {
122     *dst = *src;
123     dst->data_allocated = src->data_used;
124     dst->data_used = 0;
125     dst->data = xmalloc(dst->data_allocated);
126     copy_data(dst);
127 }
128
129 static void
130 prealloc_data(struct dhcp_msg *msg, size_t n)
131 {
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);
137         if (old_data) {
138             copy_data(msg);
139             free(old_data);
140         }
141     }
142 }
143
144 static void *
145 append_data(struct dhcp_msg *msg, const void *data, size_t n)
146 {
147     uint8_t *p = &msg->data[msg->data_used];
148     memcpy(p, data, n);
149     msg->data_used += n;
150     return p;
151 }
152
153 static void
154 copy_data(struct dhcp_msg *msg)
155 {
156     int code;
157
158     msg->data_used = 0;
159     for (code = 0; code < DHCP_N_OPTIONS; code++) {
160         struct dhcp_option *opt = &msg->options[code];
161         if (opt->data) {
162             assert(msg->data_used + opt->n <= msg->data_allocated);
163             opt->data = append_data(msg, opt->data, opt->n);
164         }
165     }
166 }
167
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). */
170 void
171 dhcp_msg_put(struct dhcp_msg *msg, int code,
172              const void *data, size_t n)
173 {
174     struct dhcp_option *opt;
175     if (code == DHCP_CODE_PAD || code == DHCP_CODE_END) {
176         return;
177     }
178
179     opt = &msg->options[code];
180     prealloc_data(msg, n + opt->n);
181     if (opt->n) {
182         if (&msg->data[msg->data_used - opt->n] != opt->data) {
183             opt->data = append_data(msg, opt->data, opt->n);
184         }
185         append_data(msg, data, n);
186     } else {
187         opt->data = append_data(msg, data, n);
188     }
189     opt->n += n;
190 }
191
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). */
195 void
196 dhcp_msg_put_bool(struct dhcp_msg *msg, int code, bool b_)
197 {
198     char b = !!b_;
199     dhcp_msg_put(msg, code, &b, 1);
200 }
201
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). */
205 void
206 dhcp_msg_put_secs(struct dhcp_msg *msg, int code, uint32_t secs_)
207 {
208     uint32_t secs = htonl(secs_);
209     dhcp_msg_put(msg, code, &secs, sizeof secs);
210 }
211
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). */
215 void
216 dhcp_msg_put_ip(struct dhcp_msg *msg, int code, uint32_t ip)
217 {
218     dhcp_msg_put(msg, code, &ip, sizeof ip);
219 }
220
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). */
223 void
224 dhcp_msg_put_string(struct dhcp_msg *msg, int code, const char *string)
225 {
226     dhcp_msg_put(msg, code, string, strlen(string));
227 }
228
229 /* Appends octet 'x' to DHCP option in 'msg' represented by 'code' (which must
230  * be in the range 0...DHCP_N_OPTIONS). */
231 void
232 dhcp_msg_put_uint8(struct dhcp_msg *msg, int code, uint8_t x)
233 {
234     dhcp_msg_put(msg, code, &x, sizeof x);
235 }
236
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)
241 {
242     dhcp_msg_put(msg, code, data, n);
243 }
244
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). */
248 void
249 dhcp_msg_put_uint16(struct dhcp_msg *msg, int code, uint16_t x_)
250 {
251     uint16_t x = htons(x_);
252     dhcp_msg_put(msg, code, &x, sizeof x);
253 }
254
255
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). */
259 void
260 dhcp_msg_put_uint16_array(struct dhcp_msg *msg, int code,
261                           const uint16_t data[], size_t n)
262 {
263     size_t i;
264
265     for (i = 0; i < n; i++) {
266         dhcp_msg_put_uint16(msg, code, data[i]);
267     }
268 }
269
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. */
274 const void *
275 dhcp_msg_get(const struct dhcp_msg *msg, int code,
276              size_t offset, size_t size)
277 {
278     const struct dhcp_option *opt = &msg->options[code];
279     return offset + size <= opt->n ? (const char *) opt->data + offset : NULL;
280 }
281
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. */
286 bool
287 dhcp_msg_get_bool(const struct dhcp_msg *msg, int code, size_t offset,
288                   bool *out)
289 {
290     const uint8_t *uint8 = dhcp_msg_get(msg, code, offset, sizeof *uint8);
291     if (uint8) {
292         *out = *uint8 != 0;
293         return true;
294     } else {
295         return false;
296     }
297 }
298
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. */
304 bool
305 dhcp_msg_get_secs(const struct dhcp_msg *msg, int code, size_t offset,
306                   uint32_t *out)
307 {
308     const uint32_t *uint32 = dhcp_msg_get(msg, code, offset * sizeof *uint32,
309                                           sizeof *uint32);
310     if (uint32) {
311         *out = ntohl(*uint32);
312         return true;
313     } else {
314         return false;
315     }
316 }
317
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
322  * + 1)' bytes. */
323 bool
324 dhcp_msg_get_ip(const struct dhcp_msg *msg, int code,
325                 size_t offset, uint32_t *out)
326 {
327     const uint32_t *uint32 = dhcp_msg_get(msg, code, offset * sizeof *uint32,
328                                           sizeof *uint32);
329     if (uint32) {
330         *out = *uint32;
331         return true;
332     } else {
333         return false;
334     }
335 }
336
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().
340  *
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.) */
344 char *
345 dhcp_msg_get_string(const struct dhcp_msg *msg, int code)
346 {
347     const struct dhcp_option *opt = &msg->options[code];
348     return opt->data ? xmemdup0(opt->data, opt->n) : NULL;
349 }
350
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'
354  * bytes. */
355 bool
356 dhcp_msg_get_uint8(const struct dhcp_msg *msg, int code,
357                    size_t offset, uint8_t *out)
358 {
359     const uint8_t *uint8 = dhcp_msg_get(msg, code, offset, sizeof *uint8);
360     if (uint8) {
361         *out = *uint8;
362         return true;
363     } else {
364         return false;
365     }
366 }
367
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)'
372  * bytes. */
373 bool
374 dhcp_msg_get_uint16(const struct dhcp_msg *msg, int code,
375                     size_t offset, uint16_t *out)
376 {
377     const uint16_t *uint16 = dhcp_msg_get(msg, code, offset * sizeof *uint16,
378                                           sizeof *uint16);
379     if (uint16) {
380         *out = ntohs(*uint16);
381         return true;
382     } else {
383         return false;
384     }
385 }
386
387 /* Appends a string representing 'duration' seconds to 'ds'. */
388 static void
389 put_duration(struct ds *ds, unsigned int duration)
390 {
391     if (duration) {
392         if (duration >= 86400) {
393             ds_put_format(ds, "%ud", duration / 86400);
394             duration %= 86400;
395         }
396         if (duration >= 3600) {
397             ds_put_format(ds, "%uh", duration / 3600);
398             duration %= 3600;
399         }
400         if (duration >= 60) {
401             ds_put_format(ds, "%umin", duration / 60);
402             duration %= 60;
403         }
404         if (duration > 0) {
405             ds_put_format(ds, "%us", duration);
406         }
407     } else {
408         ds_put_cstr(ds, "0s");
409     }
410 }
411
412 /* Appends a string representation of 'opt', which has the given 'code', to
413  * 'ds'. */
414 const char *
415 dhcp_option_to_string(const struct dhcp_option *opt, int code, struct ds *ds)
416 {
417     const struct option_class *class = get_option_class(code);
418     const struct arg_type *type = &types[class->type];
419     size_t offset;
420     const char *cp;
421
422     for (cp = class->name; *cp; cp++) {
423         unsigned char c = *cp;
424         ds_put_char(ds, c == '_' ? '-' : tolower(c));
425     }
426     ds_put_char(ds, '=');
427
428     if (!opt->data || !opt->n) {
429         ds_put_cstr(ds, opt->data ? "empty" : "null");
430         return ds_cstr(ds);
431     }
432
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, '"');
437         return ds_cstr(ds);
438     }
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;
444
445         if (offset && class->type != DHCP_ARG_STRING) {
446             ds_put_cstr(ds, class->type == DHCP_ARG_UINT8 ? ":" : ", ");
447         }
448         switch (class->type) {
449         case DHCP_ARG_FIXED:
450             NOT_REACHED();
451         case DHCP_ARG_IP:
452             ds_put_format(ds, IP_FMT, IP_ARGS(uint32));
453             break;
454         case DHCP_ARG_UINT8:
455             ds_put_format(ds, "%02"PRIx8, *uint8);
456             break;
457         case DHCP_ARG_UINT16:
458             ds_put_format(ds, "%"PRIu16, ntohs(*uint16));
459             break;
460         case DHCP_ARG_UINT32:
461             ds_put_format(ds, "%"PRIu32, ntohl(*uint32));
462             break;
463         case DHCP_ARG_SECS:
464             put_duration(ds, ntohl(*uint32));
465             break;
466         case DHCP_ARG_STRING:
467             NOT_REACHED();
468         case DHCP_ARG_BOOLEAN:
469             if (*uint8 == 0) {
470                 ds_put_cstr(ds, "false");
471             } else if (*uint8 == 1) {
472                 ds_put_cstr(ds, "true");
473             } else {
474                 ds_put_format(ds, "**%"PRIu8"**", *uint8);
475             }
476             break;
477         }
478     }
479     if (offset != opt->n) {
480         if (offset) {
481             ds_put_cstr(ds, ", ");
482         }
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);
488         }
489         ds_put_cstr(ds, "**");
490     }
491     return ds_cstr(ds);
492 }
493
494 /* Returns true if 'a' and 'b' have the same content, false otherwise. */
495 bool
496 dhcp_option_equals(const struct dhcp_option *a, const struct dhcp_option *b)
497 {
498     return ((a->data != NULL) == (b->data != NULL)
499             && a->n == b->n
500             && !memcmp(a->data, b->data, a->n));
501 }
502
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. */
506 const char *
507 dhcp_msg_to_string(const struct dhcp_msg *msg, bool multiline, struct ds *ds)
508 {
509     char separator = multiline ? '\n' : ' ';
510     int code;
511
512     ds_clear(ds);
513     ds_put_format(ds, "op=%s",
514                   (msg->op == DHCP_BOOTREQUEST ? "request"
515                    : msg->op == DHCP_BOOTREPLY ? "reply"
516                    : "error"));
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);
521     if (msg->flags) {
522         ds_put_format(ds, "%cflags=", separator);
523         if (msg->flags & DHCP_FLAGS_BROADCAST) {
524             ds_put_cstr(ds, "[BROADCAST]");
525         }
526         if (msg->flags & DHCP_FLAGS_MBZ) {
527             ds_put_format(ds, "[0x%04"PRIx16"]", msg->flags & DHCP_FLAGS_MBZ);
528         }
529     }
530     if (msg->ciaddr) {
531         ds_put_format(ds, "%cciaddr="IP_FMT, separator, IP_ARGS(&msg->ciaddr));
532     }
533     if (msg->yiaddr) {
534         ds_put_format(ds, "%cyiaddr="IP_FMT, separator, IP_ARGS(&msg->yiaddr));
535     }
536     if (msg->siaddr) {
537         ds_put_format(ds, "%csiaddr="IP_FMT, separator, IP_ARGS(&msg->siaddr));
538     }
539     if (msg->giaddr) {
540         ds_put_format(ds, "%cgiaddr="IP_FMT, separator, IP_ARGS(&msg->giaddr));
541     }
542     ds_put_format(ds, "%cchaddr="ETH_ADDR_FMT,
543                   separator, ETH_ADDR_ARGS(msg->chaddr));
544
545     for (code = 0; code < DHCP_N_OPTIONS; code++) {
546         const struct dhcp_option *opt = &msg->options[code];
547         if (opt->data) {
548             ds_put_char(ds, separator);
549             dhcp_option_to_string(opt, code, ds);
550         }
551     }
552     if (multiline) {
553         ds_put_char(ds, separator);
554     }
555     return ds_cstr(ds);
556 }
557
558 static void
559 parse_options(struct dhcp_msg *msg, const char *name, void *data, size_t size,
560               int option_offset)
561 {
562     struct ofpbuf b;
563
564     b.data = data;
565     b.size = size;
566     for (;;) {
567         uint8_t *code, *len;
568         void *payload;
569
570         code = ofpbuf_try_pull(&b, 1);
571         if (!code || *code == DHCP_CODE_END) {
572             break;
573         } else if (*code == DHCP_CODE_PAD) {
574             continue;
575         }
576
577         len = ofpbuf_try_pull(&b, 1);
578         if (!len) {
579             VLOG_DBG_RL(&rl, "reached end of %s expecting length byte", name);
580             break;
581         }
582
583         payload = ofpbuf_try_pull(&b, *len);
584         if (!payload) {
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);
588             break;
589         }
590         dhcp_msg_put(msg, *code + option_offset, payload, *len);
591     }
592 }
593
594 static void
595 validate_options(struct dhcp_msg *msg)
596 {
597     int code;
598
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];
603         if (opt->data) {
604             size_t n_elems = opt->n / type->size;
605             size_t remainder = opt->n % type->size;
606             bool ok = true;
607             if (remainder) {
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);
612                 ok = false;
613             }
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);
619                 ok = false;
620             }
621             if (!ok) {
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));
625                 ds_destroy(&ds);
626
627                 opt->n = 0;
628                 opt->data = NULL;
629             }
630         }
631     }
632 }
633
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. */
637 int
638 dhcp_parse(struct dhcp_msg *msg, const struct ofpbuf *b_)
639 {
640     struct ofpbuf b = *b_;
641     struct dhcp_header *dhcp;
642     uint32_t *cookie;
643     uint8_t type;
644     char *vendor_class;
645
646     dhcp = ofpbuf_try_pull(&b, sizeof *dhcp);
647     if (!dhcp) {
648         VLOG_DBG_RL(&rl, "buffer too small for DHCP header (%zu bytes)",
649                     b.size);
650         goto error;
651     }
652
653     if (dhcp->op != DHCP_BOOTREPLY && dhcp->op != DHCP_BOOTREQUEST) {
654         VLOG_DBG_RL(&rl, "invalid DHCP op (%"PRIu8")", dhcp->op);
655         goto error;
656     }
657     if (dhcp->htype != ARP_HRD_ETHERNET) {
658         VLOG_DBG_RL(&rl, "invalid DHCP htype (%"PRIu8")", dhcp->htype);
659         goto error;
660     }
661     if (dhcp->hlen != ETH_ADDR_LEN) {
662         VLOG_DBG_RL(&rl, "invalid DHCP hlen (%"PRIu8")", dhcp->hlen);
663         goto error;
664     }
665
666     dhcp_msg_init(msg);
667     msg->op = dhcp->op;
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);
676
677     cookie = ofpbuf_try_pull(&b, sizeof cookie);
678     if (cookie) {
679         if (ntohl(*cookie) == DHCP_OPTS_COOKIE) {
680             uint8_t overload;
681
682             parse_options(msg, "options", b.data, b.size, 0);
683             if (dhcp_msg_get_uint8(msg, DHCP_CODE_OPTION_OVERLOAD,
684                                    0, &overload)) {
685                 if (overload & 1) {
686                     parse_options(msg, "file", dhcp->file, sizeof dhcp->file,
687                                   0);
688                 }
689                 if (overload & 2) {
690                     parse_options(msg, "sname",
691                                   dhcp->sname, sizeof dhcp->sname, 0);
692                 }
693             }
694         } else {
695             VLOG_DBG_RL(&rl, "bad DHCP options cookie: %08"PRIx32,
696                         ntohl(*cookie));
697         }
698     } else {
699         VLOG_DBG_RL(&rl, "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_RL(&rl, "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_RL(&rl, "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_RL(&rl, "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 ofpbuf *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     ofpbuf_put(b, header, sizeof header);
746     ofpbuf_put(b, data, n);
747 }
748
749 static void
750 put_option(struct ofpbuf *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 ofpbuf *b)
775 {
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;
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     ofpbuf_put(b, &dhcp, sizeof dhcp);
796     ofpbuf_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     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);
816     }
817     if (vnd_data.size) {
818         put_option(b, DHCP_CODE_VENDOR_SPECIFIC, vnd_data.data, vnd_data.size);
819     }
820     ofpbuf_uninit(&vnd_data);
821
822     /* Put end-of-options option. */
823     ofpbuf_put(b, &end, sizeof end);
824 }
825