1*f3a8c5fdSkrw /* $OpenBSD: options.c,v 1.60 2013/12/14 16:06:42 krw Exp $ */ 29a2590e5Sderaadt 3e7eb2effShenning /* DHCP options parsing and reassembly. */ 49a2590e5Sderaadt 59a2590e5Sderaadt /* 69a2590e5Sderaadt * Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium. 79a2590e5Sderaadt * All rights reserved. 89a2590e5Sderaadt * 99a2590e5Sderaadt * Redistribution and use in source and binary forms, with or without 109a2590e5Sderaadt * modification, are permitted provided that the following conditions 119a2590e5Sderaadt * are met: 129a2590e5Sderaadt * 139a2590e5Sderaadt * 1. Redistributions of source code must retain the above copyright 149a2590e5Sderaadt * notice, this list of conditions and the following disclaimer. 159a2590e5Sderaadt * 2. Redistributions in binary form must reproduce the above copyright 169a2590e5Sderaadt * notice, this list of conditions and the following disclaimer in the 179a2590e5Sderaadt * documentation and/or other materials provided with the distribution. 189a2590e5Sderaadt * 3. Neither the name of The Internet Software Consortium nor the names 199a2590e5Sderaadt * of its contributors may be used to endorse or promote products derived 209a2590e5Sderaadt * from this software without specific prior written permission. 219a2590e5Sderaadt * 229a2590e5Sderaadt * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND 239a2590e5Sderaadt * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 249a2590e5Sderaadt * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 259a2590e5Sderaadt * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 269a2590e5Sderaadt * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR 279a2590e5Sderaadt * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 289a2590e5Sderaadt * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 299a2590e5Sderaadt * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 309a2590e5Sderaadt * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 319a2590e5Sderaadt * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 329a2590e5Sderaadt * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 339a2590e5Sderaadt * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 349a2590e5Sderaadt * SUCH DAMAGE. 359a2590e5Sderaadt * 369a2590e5Sderaadt * This software has been written for the Internet Software Consortium 379a2590e5Sderaadt * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie 389a2590e5Sderaadt * Enterprises. To learn more about the Internet Software Consortium, 399a2590e5Sderaadt * see ``http://www.vix.com/isc''. To learn more about Vixie 409a2590e5Sderaadt * Enterprises, see ``http://www.vix.com''. 419a2590e5Sderaadt */ 429a2590e5Sderaadt 439a2590e5Sderaadt #include "dhcpd.h" 449a2590e5Sderaadt 4502e02bd5Skrw int parse_option_buffer(struct option_data *, unsigned char *, int); 469a2590e5Sderaadt 47c714dadcShenning /* 48c714dadcShenning * Parse options out of the specified buffer, storing addresses of 4992018899Skrw * option values in options. Return 0 if errors, 1 if not. 50c714dadcShenning */ 5102e02bd5Skrw int 524f062ee3Skrw parse_option_buffer(struct option_data *options, unsigned char *buffer, 534f062ee3Skrw int length) 549a2590e5Sderaadt { 55285f06efSderaadt unsigned char *s, *t, *end = buffer + length; 56285f06efSderaadt int len, code; 579a2590e5Sderaadt 589a2590e5Sderaadt for (s = buffer; *s != DHO_END && s < end; ) { 599a2590e5Sderaadt code = s[0]; 609a2590e5Sderaadt 619a2590e5Sderaadt /* Pad options don't have a length - just skip them. */ 629a2590e5Sderaadt if (code == DHO_PAD) { 63f1e89499Shenning s++; 649a2590e5Sderaadt continue; 659a2590e5Sderaadt } 669a2590e5Sderaadt 67c714dadcShenning /* 6899c003b1Skrw * All options other than DHO_PAD and DHO_END have a one-byte 6999c003b1Skrw * length field. It could be 0! Make sure that the length byte 7099c003b1Skrw * is present, and all the data is available. 71c714dadcShenning */ 7299c003b1Skrw if (s + 1 < end) { 739a2590e5Sderaadt len = s[1]; 7499c003b1Skrw if (s + 1 + len < end) { 7599c003b1Skrw ; /* option data is all there. */ 7699c003b1Skrw } else { 77b6fc88b9Skrw warning("option %s (%d) larger than buffer.", 78b6fc88b9Skrw dhcp_options[code].name, len); 7902e02bd5Skrw return (0); 809a2590e5Sderaadt } 8199c003b1Skrw } else { 8299c003b1Skrw warning("option %s has no length field.", 8399c003b1Skrw dhcp_options[code].name); 8499c003b1Skrw return (0); 8599c003b1Skrw } 86df453039Skrw 87df453039Skrw /* 88df453039Skrw * Strip trailing NULs from ascii ('t') options. They 89df453039Skrw * will be treated as DHO_PAD options. i.e. ignored. RFC 2132 90df453039Skrw * says "Options containing NVT ASCII data SHOULD NOT include 91df453039Skrw * a trailing NULL; however, the receiver of such options 92df453039Skrw * MUST be prepared to delete trailing nulls if they exist." 93df453039Skrw */ 94df453039Skrw if (dhcp_options[code].format[0] == 't') { 9599c003b1Skrw while (len > 0 && s[len + 1] == '\0') 9699c003b1Skrw len--; 97df453039Skrw } 98df453039Skrw 99c714dadcShenning /* 100c714dadcShenning * If we haven't seen this option before, just make 101c714dadcShenning * space for it and copy it there. 102c714dadcShenning */ 1034f062ee3Skrw if (!options[code].data) { 1048e916ab9Shenning if (!(t = calloc(1, len + 1))) 1059a2590e5Sderaadt error("Can't allocate storage for option %s.", 1069a2590e5Sderaadt dhcp_options[code].name); 107c714dadcShenning /* 108c714dadcShenning * Copy and NUL-terminate the option (in case 109cff08477Sstevesk * it's an ASCII string). 110c714dadcShenning */ 1119a2590e5Sderaadt memcpy(t, &s[2], len); 1129a2590e5Sderaadt t[len] = 0; 1134f062ee3Skrw options[code].len = len; 1144f062ee3Skrw options[code].data = t; 1159a2590e5Sderaadt } else { 116c714dadcShenning /* 117c714dadcShenning * If it's a repeat, concatenate it to whatever 11892018899Skrw * we last saw. 119c714dadcShenning */ 1204f062ee3Skrw t = calloc(1, len + options[code].len + 1); 1219a2590e5Sderaadt if (!t) 1229a2590e5Sderaadt error("Can't expand storage for option %s.", 1239a2590e5Sderaadt dhcp_options[code].name); 1244f062ee3Skrw memcpy(t, options[code].data, options[code].len); 1254f062ee3Skrw memcpy(t + options[code].len, &s[2], len); 1264f062ee3Skrw options[code].len += len; 1274f062ee3Skrw t[options[code].len] = 0; 1284f062ee3Skrw free(options[code].data); 1294f062ee3Skrw options[code].data = t; 1309a2590e5Sderaadt } 1319a2590e5Sderaadt s += len + 2; 1329a2590e5Sderaadt } 13302e02bd5Skrw 13402e02bd5Skrw return (1); 1359a2590e5Sderaadt } 1369a2590e5Sderaadt 137c714dadcShenning /* 13896978980Skrw * Copy as many options as fit in buflen bytes of buf. Return the 13996978980Skrw * offset of the start of the last option copied. A caller can check 14096978980Skrw * to see if it's DHO_END to decide if all the options were copied. 141c714dadcShenning */ 142c714dadcShenning int 143d6a67f0fSkrw cons_options(struct option_data *options) 1449a2590e5Sderaadt { 145e7cf2d10Skrw unsigned char *buf = client->bootrequest_packet.options; 146d6a67f0fSkrw int buflen = 576 - DHCP_FIXED_LEN; 14796978980Skrw int ix, incr, length, bufix, code, lastopt = -1; 1489a2590e5Sderaadt 149736b0ed2Skrw memset(buf, 0, buflen); 1509a2590e5Sderaadt 15196978980Skrw memcpy(buf, DHCP_OPTIONS_COOKIE, 4); 152d6a67f0fSkrw if (options[DHO_DHCP_MESSAGE_TYPE].data) { 153d6a67f0fSkrw memcpy(&buf[4], DHCP_OPTIONS_MESSAGE_TYPE, 3); 154d6a67f0fSkrw buf[6] = options[DHO_DHCP_MESSAGE_TYPE].data[0]; 155d6a67f0fSkrw bufix = 7; 156d6a67f0fSkrw } else 15796978980Skrw bufix = 4; 1589a2590e5Sderaadt 15996978980Skrw for (code = DHO_SUBNET_MASK; code < DHO_END; code++) { 160d6a67f0fSkrw if (!options[code].data || code == DHO_DHCP_MESSAGE_TYPE) 1619a2590e5Sderaadt continue; 1629a2590e5Sderaadt 163d7d9bbf5Skrw length = options[code].len; 16496978980Skrw if (bufix + length + 2*((length+254)/255) >= buflen) 16596978980Skrw return (lastopt); 1669a2590e5Sderaadt 16796978980Skrw lastopt = bufix; 1689a2590e5Sderaadt ix = 0; 1699a2590e5Sderaadt 1709a2590e5Sderaadt while (length) { 17196978980Skrw incr = length > 255 ? 255 : length; 1729a2590e5Sderaadt 17396978980Skrw buf[bufix++] = code; 17496978980Skrw buf[bufix++] = incr; 17596978980Skrw memcpy(buf + bufix, options[code].data + ix, incr); 1769a2590e5Sderaadt 1779a2590e5Sderaadt length -= incr; 1789a2590e5Sderaadt ix += incr; 1796fc9f4f6Skrw bufix += incr; 1809a2590e5Sderaadt } 1819a2590e5Sderaadt } 18296978980Skrw 18396978980Skrw if (bufix < buflen) { 18496978980Skrw buf[bufix] = DHO_END; 18596978980Skrw lastopt = bufix; 18696978980Skrw } 18796978980Skrw 18896978980Skrw return (lastopt); 1899a2590e5Sderaadt } 1909a2590e5Sderaadt 191c714dadcShenning /* 192c714dadcShenning * Format the specified option so that a human can easily read it. 193c714dadcShenning */ 194c714dadcShenning char * 195acf4c28bSkrw pretty_print_option(unsigned int code, struct option_data *option, 196acf4c28bSkrw int emit_punct) 1979a2590e5Sderaadt { 1989a2590e5Sderaadt static char optbuf[32768]; /* XXX */ 199285f06efSderaadt int hunksize = 0, numhunk = -1, numelem = 0; 200285f06efSderaadt char fmtbuf[32], *op = optbuf; 201285f06efSderaadt int i, j, k, opleft = sizeof(optbuf); 202acf4c28bSkrw unsigned char *data = option->data; 2039a2590e5Sderaadt unsigned char *dp = data; 204acf4c28bSkrw int len = option->len; 205*f3a8c5fdSkrw int opcount = 0; 2069a2590e5Sderaadt struct in_addr foo; 2079a2590e5Sderaadt char comma; 2089a2590e5Sderaadt 2092f18daabSkrw memset(optbuf, 0, sizeof(optbuf)); 2102f18daabSkrw 2119a2590e5Sderaadt /* Code should be between 0 and 255. */ 2122f18daabSkrw if (code > 255) { 2132f18daabSkrw warning("pretty_print_option: bad code %d", code); 2142f18daabSkrw goto done; 2152f18daabSkrw } 2169a2590e5Sderaadt 217acf4c28bSkrw if (emit_punct) 2189a2590e5Sderaadt comma = ','; 2199a2590e5Sderaadt else 2209a2590e5Sderaadt comma = ' '; 2219a2590e5Sderaadt 2229a2590e5Sderaadt /* Figure out the size of the data. */ 2239a2590e5Sderaadt for (i = 0; dhcp_options[code].format[i]; i++) { 2249a2590e5Sderaadt if (!numhunk) { 225c955dd46Smickey warning("%s: Excess information in format string: %s", 2269a2590e5Sderaadt dhcp_options[code].name, 2279a2590e5Sderaadt &(dhcp_options[code].format[i])); 2282f18daabSkrw goto done; 2299a2590e5Sderaadt } 2309a2590e5Sderaadt numelem++; 2319a2590e5Sderaadt fmtbuf[i] = dhcp_options[code].format[i]; 2329a2590e5Sderaadt switch (dhcp_options[code].format[i]) { 2339a2590e5Sderaadt case 'A': 2349a2590e5Sderaadt --numelem; 2359a2590e5Sderaadt fmtbuf[i] = 0; 2369a2590e5Sderaadt numhunk = 0; 23729432cd9Sphessler if (hunksize == 0) { 23829432cd9Sphessler warning("%s: no size indicator before A" 23929432cd9Sphessler " in format string: %s", 24029432cd9Sphessler dhcp_options[code].name, 24129432cd9Sphessler dhcp_options[code].format); 2422f18daabSkrw goto done; 24329432cd9Sphessler } 2449a2590e5Sderaadt break; 2459a2590e5Sderaadt case 'X': 246c714dadcShenning for (k = 0; k < len; k++) 2479a2590e5Sderaadt if (!isascii(data[k]) || 2489a2590e5Sderaadt !isprint(data[k])) 2499a2590e5Sderaadt break; 250b54c879eShenning if (k == len) { 2519a2590e5Sderaadt fmtbuf[i] = 't'; 2529a2590e5Sderaadt numhunk = -2; 2539a2590e5Sderaadt } else { 2549a2590e5Sderaadt fmtbuf[i] = 'x'; 2559a2590e5Sderaadt hunksize++; 2569a2590e5Sderaadt comma = ':'; 2579a2590e5Sderaadt numhunk = 0; 2589a2590e5Sderaadt } 2599a2590e5Sderaadt fmtbuf[i + 1] = 0; 2609a2590e5Sderaadt break; 2619a2590e5Sderaadt case 't': 2629a2590e5Sderaadt fmtbuf[i] = 't'; 2639a2590e5Sderaadt fmtbuf[i + 1] = 0; 2649a2590e5Sderaadt numhunk = -2; 2659a2590e5Sderaadt break; 2669a2590e5Sderaadt case 'I': 2679a2590e5Sderaadt case 'l': 2689a2590e5Sderaadt case 'L': 2699a2590e5Sderaadt hunksize += 4; 2709a2590e5Sderaadt break; 2719a2590e5Sderaadt case 's': 2729a2590e5Sderaadt case 'S': 2739a2590e5Sderaadt hunksize += 2; 2749a2590e5Sderaadt break; 2759a2590e5Sderaadt case 'b': 2769a2590e5Sderaadt case 'B': 2779a2590e5Sderaadt case 'f': 2789a2590e5Sderaadt hunksize++; 2799a2590e5Sderaadt break; 2809a2590e5Sderaadt case 'e': 2819a2590e5Sderaadt break; 2829a2590e5Sderaadt default: 283c955dd46Smickey warning("%s: garbage in format string: %s", 2849a2590e5Sderaadt dhcp_options[code].name, 2859a2590e5Sderaadt &(dhcp_options[code].format[i])); 2862f18daabSkrw goto done; 2879a2590e5Sderaadt } 2889a2590e5Sderaadt } 2899a2590e5Sderaadt 290d22f105fSkrw /* Check for too few bytes. */ 2919a2590e5Sderaadt if (hunksize > len) { 292c955dd46Smickey warning("%s: expecting at least %d bytes; got %d", 293c714dadcShenning dhcp_options[code].name, hunksize, len); 2942f18daabSkrw goto done; 2959a2590e5Sderaadt } 296d22f105fSkrw /* Check for too many bytes. */ 2972f18daabSkrw if (numhunk == -1 && hunksize < len) { 29828f2359aSkrw warning("%s: expecting only %d bytes: got %d", 29928f2359aSkrw dhcp_options[code].name, hunksize, len); 3002f18daabSkrw goto done; 3012f18daabSkrw } 3029a2590e5Sderaadt 3039a2590e5Sderaadt /* If this is an array, compute its size. */ 3049a2590e5Sderaadt if (!numhunk) 3059a2590e5Sderaadt numhunk = len / hunksize; 3069a2590e5Sderaadt /* See if we got an exact number of hunks. */ 3072f18daabSkrw if (numhunk > 0 && numhunk * hunksize != len) { 3082f18daabSkrw warning("%s: expecting %d bytes: got %d", 3092f18daabSkrw dhcp_options[code].name, numhunk * hunksize, len); 3102f18daabSkrw goto done; 3112f18daabSkrw } 3129a2590e5Sderaadt 3139a2590e5Sderaadt /* A one-hunk array prints the same as a single hunk. */ 3149a2590e5Sderaadt if (numhunk < 0) 3159a2590e5Sderaadt numhunk = 1; 3169a2590e5Sderaadt 3179a2590e5Sderaadt /* Cycle through the array (or hunk) printing the data. */ 3189a2590e5Sderaadt for (i = 0; i < numhunk; i++) { 3199a2590e5Sderaadt for (j = 0; j < numelem; j++) { 3209a2590e5Sderaadt switch (fmtbuf[j]) { 3219a2590e5Sderaadt case 't': 322acf4c28bSkrw if (emit_punct) { 323*f3a8c5fdSkrw opcount = snprintf(op, opleft, "\""); 324*f3a8c5fdSkrw if (opcount >= opleft || opcount == -1) 325*f3a8c5fdSkrw goto toobig; 326*f3a8c5fdSkrw opleft -= opcount; 327*f3a8c5fdSkrw op += opcount; 3289a2590e5Sderaadt } 3299a2590e5Sderaadt for (; dp < data + len; dp++) { 330*f3a8c5fdSkrw if (!isascii(*dp) || !isprint(*dp)) { 331*f3a8c5fdSkrw opcount = snprintf(op, opleft, 3329a2590e5Sderaadt "\\%03o", *dp); 3339a2590e5Sderaadt } else if (*dp == '"' || 3349a2590e5Sderaadt *dp == '\'' || 3359a2590e5Sderaadt *dp == '$' || 3369a2590e5Sderaadt *dp == '`' || 3379a2590e5Sderaadt *dp == '\\') { 338*f3a8c5fdSkrw opcount = snprintf(op, opleft, 339*f3a8c5fdSkrw "\\%c", *dp); 3409a2590e5Sderaadt } else { 341*f3a8c5fdSkrw opcount = snprintf(op, opleft, 342*f3a8c5fdSkrw "%c", *dp); 3439a2590e5Sderaadt } 344*f3a8c5fdSkrw if (opcount >= opleft || opcount == -1) 345*f3a8c5fdSkrw goto toobig; 346*f3a8c5fdSkrw opleft -= opcount; 347*f3a8c5fdSkrw op += opcount; 3489a2590e5Sderaadt } 349acf4c28bSkrw if (emit_punct) { 350*f3a8c5fdSkrw opcount = snprintf(op, opleft, "\""); 351*f3a8c5fdSkrw if (opcount >= opleft || opcount == -1) 352*f3a8c5fdSkrw goto toobig; 353*f3a8c5fdSkrw opleft -= opcount; 354*f3a8c5fdSkrw op += opcount; 3559a2590e5Sderaadt } 356*f3a8c5fdSkrw opcount = 0; /* Already moved dp & op. */ 3579a2590e5Sderaadt break; 3589a2590e5Sderaadt case 'I': 3599a2590e5Sderaadt foo.s_addr = htonl(getULong(dp)); 360*f3a8c5fdSkrw opcount = snprintf(op, opleft, "%s", 361*f3a8c5fdSkrw inet_ntoa(foo)); 3629a2590e5Sderaadt dp += 4; 3639a2590e5Sderaadt break; 3649a2590e5Sderaadt case 'l': 3659a2590e5Sderaadt opcount = snprintf(op, opleft, "%ld", 3669a2590e5Sderaadt (long)getLong(dp)); 3679a2590e5Sderaadt dp += 4; 3689a2590e5Sderaadt break; 3699a2590e5Sderaadt case 'L': 3709a2590e5Sderaadt opcount = snprintf(op, opleft, "%ld", 3719a2590e5Sderaadt (unsigned long)getULong(dp)); 3729a2590e5Sderaadt dp += 4; 3739a2590e5Sderaadt break; 3749a2590e5Sderaadt case 's': 3759a2590e5Sderaadt opcount = snprintf(op, opleft, "%d", 3769a2590e5Sderaadt getShort(dp)); 3779a2590e5Sderaadt dp += 2; 3789a2590e5Sderaadt break; 3799a2590e5Sderaadt case 'S': 3809a2590e5Sderaadt opcount = snprintf(op, opleft, "%d", 3819a2590e5Sderaadt getUShort(dp)); 3829a2590e5Sderaadt dp += 2; 3839a2590e5Sderaadt break; 3849a2590e5Sderaadt case 'b': 3859a2590e5Sderaadt opcount = snprintf(op, opleft, "%d", 386de3ca9dbSkrw *(char *)dp); 387de3ca9dbSkrw dp++; 3889a2590e5Sderaadt break; 3899a2590e5Sderaadt case 'B': 390de3ca9dbSkrw opcount = snprintf(op, opleft, "%d", *dp); 391de3ca9dbSkrw dp++; 3929a2590e5Sderaadt break; 3939a2590e5Sderaadt case 'x': 394de3ca9dbSkrw opcount = snprintf(op, opleft, "%x", *dp); 395de3ca9dbSkrw dp++; 3969a2590e5Sderaadt break; 3979a2590e5Sderaadt case 'f': 398*f3a8c5fdSkrw opcount = snprintf(op, opleft, "%s", 399*f3a8c5fdSkrw *dp ? "true" : "false"); 400de3ca9dbSkrw dp++; 4019a2590e5Sderaadt break; 4029a2590e5Sderaadt default: 403c955dd46Smickey warning("Unexpected format code %c", fmtbuf[j]); 4049a2590e5Sderaadt goto toobig; 405*f3a8c5fdSkrw } 406*f3a8c5fdSkrw if (opcount >= opleft || opcount == -1) 407*f3a8c5fdSkrw goto toobig; 408*f3a8c5fdSkrw opleft -= opcount; 409*f3a8c5fdSkrw op += opcount; 4109a2590e5Sderaadt if (j + 1 < numelem && comma != ':') { 411*f3a8c5fdSkrw opcount = snprintf(op, opleft, " "); 412*f3a8c5fdSkrw if (opcount >= opleft || opcount == -1) 413*f3a8c5fdSkrw goto toobig; 414*f3a8c5fdSkrw opleft -= opcount; 415*f3a8c5fdSkrw op += opcount; 4169a2590e5Sderaadt } 4179a2590e5Sderaadt } 4189a2590e5Sderaadt if (i + 1 < numhunk) { 419*f3a8c5fdSkrw opcount = snprintf(op, opleft, "%c", comma); 420*f3a8c5fdSkrw if (opcount >= opleft || opcount == -1) 4219a2590e5Sderaadt goto toobig; 422*f3a8c5fdSkrw opleft -= opcount; 423*f3a8c5fdSkrw op += opcount; 424*f3a8c5fdSkrw } 4259a2590e5Sderaadt } 4262f18daabSkrw 4272f18daabSkrw done: 428c714dadcShenning return (optbuf); 4292f18daabSkrw 4309a2590e5Sderaadt toobig: 4312f18daabSkrw memset(optbuf, 0, sizeof(optbuf)); 4322f18daabSkrw return (optbuf); 4339a2590e5Sderaadt } 4349a2590e5Sderaadt 435c714dadcShenning void 436917d8addSkrw do_packet(unsigned int from_port, struct in_addr from, 437393831bbSkrw struct ether_addr *hfrom) 4389a2590e5Sderaadt { 43902e02bd5Skrw struct dhcp_packet *packet = &client->packet; 4404f062ee3Skrw struct option_data options[256]; 441b21b72f8Skrw struct reject_elem *ap; 4426896c986Skrw void (*handler)(struct in_addr, struct option_data *, char *); 4436896c986Skrw char *type, *info; 4446896c986Skrw int i, rslt, options_valid = 1; 4459a2590e5Sderaadt 446393831bbSkrw if (packet->hlen != ETHER_ADDR_LEN) { 447aff84b99Skrw #ifdef DEBUG 448aff84b99Skrw debug("Discarding packet with hlen != %s (%u)", 449aff84b99Skrw ifi->name, packet->hlen); 450aff84b99Skrw #endif 451aff84b99Skrw return; 452393831bbSkrw } else if (memcmp(&ifi->hw_address, packet->chaddr, 453393831bbSkrw sizeof(ifi->hw_address))) { 454aff84b99Skrw #ifdef DEBUG 455aff84b99Skrw debug("Discarding packet with chaddr != %s (%s)", ifi->name, 456aff84b99Skrw ether_ntoa((struct ether_addr *)packet->chaddr)); 457aff84b99Skrw #endif 4589a2590e5Sderaadt return; 4599a2590e5Sderaadt } 4609a2590e5Sderaadt 461aff84b99Skrw if (client->xid != client->packet.xid) { 462aff84b99Skrw #ifdef DEBUG 463aff84b99Skrw debug("Discarding packet with XID != %u (%u)", client->xid, 464aff84b99Skrw client->packet.xid); 465aff84b99Skrw #endif 46602e02bd5Skrw return; 467aff84b99Skrw } 468aff84b99Skrw 469aff84b99Skrw for (ap = config->reject_list; ap; ap = ap->next) 470aff84b99Skrw if (from.s_addr == ap->addr.s_addr) { 471aff84b99Skrw #ifdef DEBUG 472aff84b99Skrw debug("Discarding packet from address on reject list " 473aff84b99Skrw "(%s)", inet_ntoa(from)); 474aff84b99Skrw #endif 475aff84b99Skrw return; 476aff84b99Skrw } 4779a2590e5Sderaadt 47802e02bd5Skrw memset(options, 0, sizeof(options)); 47902e02bd5Skrw 48002e02bd5Skrw if (memcmp(&packet->options, DHCP_OPTIONS_COOKIE, 4) == 0) { 48102e02bd5Skrw /* Parse the BOOTP/DHCP options field. */ 48202e02bd5Skrw options_valid = parse_option_buffer(options, 48302e02bd5Skrw &packet->options[4], sizeof(packet->options) - 4); 48402e02bd5Skrw 48502e02bd5Skrw /* Only DHCP packets have overload areas for options. */ 48602e02bd5Skrw if (options_valid && 48702e02bd5Skrw options[DHO_DHCP_MESSAGE_TYPE].data && 48802e02bd5Skrw options[DHO_DHCP_OPTION_OVERLOAD].data) { 48902e02bd5Skrw if (options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 1) 49002e02bd5Skrw options_valid = parse_option_buffer(options, 49102e02bd5Skrw (unsigned char *)packet->file, 49202e02bd5Skrw sizeof(packet->file)); 49302e02bd5Skrw if (options_valid && 49402e02bd5Skrw options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 2) 49502e02bd5Skrw options_valid = parse_option_buffer(options, 49602e02bd5Skrw (unsigned char *)packet->sname, 49702e02bd5Skrw sizeof(packet->sname)); 49802e02bd5Skrw } 49902e02bd5Skrw } 50002e02bd5Skrw 5016896c986Skrw type = "<unknown>"; 50202e02bd5Skrw handler = NULL; 50302e02bd5Skrw 5044f062ee3Skrw if (options[DHO_DHCP_MESSAGE_TYPE].data) { 50502e02bd5Skrw /* Always try a DHCP packet, even if a bad option was seen. */ 50602e02bd5Skrw switch (options[DHO_DHCP_MESSAGE_TYPE].data[0]) { 50702e02bd5Skrw case DHCPOFFER: 50802e02bd5Skrw handler = dhcpoffer; 50902e02bd5Skrw type = "DHCPOFFER"; 51002e02bd5Skrw break; 51102e02bd5Skrw case DHCPNAK: 51202e02bd5Skrw handler = dhcpnak; 51302e02bd5Skrw type = "DHCPNACK"; 51402e02bd5Skrw break; 51502e02bd5Skrw case DHCPACK: 51602e02bd5Skrw handler = dhcpack; 51702e02bd5Skrw type = "DHCPACK"; 51802e02bd5Skrw break; 51902e02bd5Skrw default: 520aff84b99Skrw #ifdef DEBUG 521aff84b99Skrw debug("Discarding DHCP packet of unknown type (%d)", 522aff84b99Skrw options[DHO_DHCP_MESSAGE_TYPE].data[0]); 523aff84b99Skrw #endif 52402e02bd5Skrw break; 52502e02bd5Skrw } 52602e02bd5Skrw } else if (options_valid && packet->op == BOOTREPLY) { 52702e02bd5Skrw handler = dhcpoffer; 52802e02bd5Skrw type = "BOOTREPLY"; 529aff84b99Skrw } else { 530aff84b99Skrw #ifdef DEBUG 531aff84b99Skrw debug("Discarding packet which is neither DHCP nor BOOTP"); 532aff84b99Skrw #endif 53302e02bd5Skrw } 5349a2590e5Sderaadt 5356896c986Skrw rslt = asprintf(&info, "%s from %s (%s)", type, inet_ntoa(from), 536393831bbSkrw ether_ntoa(hfrom)); 5376896c986Skrw if (rslt == -1) 5386896c986Skrw error("no memory for info string"); 5396896c986Skrw 54002e02bd5Skrw if (handler) 5416896c986Skrw (*handler)(from, options, info); 5426896c986Skrw 5436896c986Skrw free(info); 54402e02bd5Skrw 545c714dadcShenning for (i = 0; i < 256; i++) 5464f062ee3Skrw if (options[i].len && options[i].data) 5474f062ee3Skrw free(options[i].data); 5489a2590e5Sderaadt } 549