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