1*8d2bd14bSkrw /* $OpenBSD: options.c,v 1.61 2013/12/18 00:37:59 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 45*8d2bd14bSkrw #include <vis.h> 46*8d2bd14bSkrw 4702e02bd5Skrw int parse_option_buffer(struct option_data *, unsigned char *, int); 489a2590e5Sderaadt 49c714dadcShenning /* 50c714dadcShenning * Parse options out of the specified buffer, storing addresses of 5192018899Skrw * option values in options. Return 0 if errors, 1 if not. 52c714dadcShenning */ 5302e02bd5Skrw int 544f062ee3Skrw parse_option_buffer(struct option_data *options, unsigned char *buffer, 554f062ee3Skrw int length) 569a2590e5Sderaadt { 57285f06efSderaadt unsigned char *s, *t, *end = buffer + length; 58285f06efSderaadt int len, code; 599a2590e5Sderaadt 609a2590e5Sderaadt for (s = buffer; *s != DHO_END && s < end; ) { 619a2590e5Sderaadt code = s[0]; 629a2590e5Sderaadt 639a2590e5Sderaadt /* Pad options don't have a length - just skip them. */ 649a2590e5Sderaadt if (code == DHO_PAD) { 65f1e89499Shenning s++; 669a2590e5Sderaadt continue; 679a2590e5Sderaadt } 689a2590e5Sderaadt 69c714dadcShenning /* 7099c003b1Skrw * All options other than DHO_PAD and DHO_END have a one-byte 7199c003b1Skrw * length field. It could be 0! Make sure that the length byte 7299c003b1Skrw * is present, and all the data is available. 73c714dadcShenning */ 7499c003b1Skrw if (s + 1 < end) { 759a2590e5Sderaadt len = s[1]; 7699c003b1Skrw if (s + 1 + len < end) { 7799c003b1Skrw ; /* option data is all there. */ 7899c003b1Skrw } else { 79b6fc88b9Skrw warning("option %s (%d) larger than buffer.", 80b6fc88b9Skrw dhcp_options[code].name, len); 8102e02bd5Skrw return (0); 829a2590e5Sderaadt } 8399c003b1Skrw } else { 8499c003b1Skrw warning("option %s has no length field.", 8599c003b1Skrw dhcp_options[code].name); 8699c003b1Skrw return (0); 8799c003b1Skrw } 88df453039Skrw 89df453039Skrw /* 90df453039Skrw * Strip trailing NULs from ascii ('t') options. They 91df453039Skrw * will be treated as DHO_PAD options. i.e. ignored. RFC 2132 92df453039Skrw * says "Options containing NVT ASCII data SHOULD NOT include 93df453039Skrw * a trailing NULL; however, the receiver of such options 94df453039Skrw * MUST be prepared to delete trailing nulls if they exist." 95df453039Skrw */ 96df453039Skrw if (dhcp_options[code].format[0] == 't') { 9799c003b1Skrw while (len > 0 && s[len + 1] == '\0') 9899c003b1Skrw len--; 99df453039Skrw } 100df453039Skrw 101c714dadcShenning /* 102c714dadcShenning * If we haven't seen this option before, just make 103c714dadcShenning * space for it and copy it there. 104c714dadcShenning */ 1054f062ee3Skrw if (!options[code].data) { 1068e916ab9Shenning if (!(t = calloc(1, len + 1))) 1079a2590e5Sderaadt error("Can't allocate storage for option %s.", 1089a2590e5Sderaadt dhcp_options[code].name); 109c714dadcShenning /* 110c714dadcShenning * Copy and NUL-terminate the option (in case 111cff08477Sstevesk * it's an ASCII string). 112c714dadcShenning */ 1139a2590e5Sderaadt memcpy(t, &s[2], len); 1149a2590e5Sderaadt t[len] = 0; 1154f062ee3Skrw options[code].len = len; 1164f062ee3Skrw options[code].data = t; 1179a2590e5Sderaadt } else { 118c714dadcShenning /* 119c714dadcShenning * If it's a repeat, concatenate it to whatever 12092018899Skrw * we last saw. 121c714dadcShenning */ 1224f062ee3Skrw t = calloc(1, len + options[code].len + 1); 1239a2590e5Sderaadt if (!t) 1249a2590e5Sderaadt error("Can't expand storage for option %s.", 1259a2590e5Sderaadt dhcp_options[code].name); 1264f062ee3Skrw memcpy(t, options[code].data, options[code].len); 1274f062ee3Skrw memcpy(t + options[code].len, &s[2], len); 1284f062ee3Skrw options[code].len += len; 1294f062ee3Skrw t[options[code].len] = 0; 1304f062ee3Skrw free(options[code].data); 1314f062ee3Skrw options[code].data = t; 1329a2590e5Sderaadt } 1339a2590e5Sderaadt s += len + 2; 1349a2590e5Sderaadt } 13502e02bd5Skrw 13602e02bd5Skrw return (1); 1379a2590e5Sderaadt } 1389a2590e5Sderaadt 139c714dadcShenning /* 14096978980Skrw * Copy as many options as fit in buflen bytes of buf. Return the 14196978980Skrw * offset of the start of the last option copied. A caller can check 14296978980Skrw * to see if it's DHO_END to decide if all the options were copied. 143c714dadcShenning */ 144c714dadcShenning int 145d6a67f0fSkrw cons_options(struct option_data *options) 1469a2590e5Sderaadt { 147e7cf2d10Skrw unsigned char *buf = client->bootrequest_packet.options; 148d6a67f0fSkrw int buflen = 576 - DHCP_FIXED_LEN; 14996978980Skrw int ix, incr, length, bufix, code, lastopt = -1; 1509a2590e5Sderaadt 151736b0ed2Skrw memset(buf, 0, buflen); 1529a2590e5Sderaadt 15396978980Skrw memcpy(buf, DHCP_OPTIONS_COOKIE, 4); 154d6a67f0fSkrw if (options[DHO_DHCP_MESSAGE_TYPE].data) { 155d6a67f0fSkrw memcpy(&buf[4], DHCP_OPTIONS_MESSAGE_TYPE, 3); 156d6a67f0fSkrw buf[6] = options[DHO_DHCP_MESSAGE_TYPE].data[0]; 157d6a67f0fSkrw bufix = 7; 158d6a67f0fSkrw } else 15996978980Skrw bufix = 4; 1609a2590e5Sderaadt 16196978980Skrw for (code = DHO_SUBNET_MASK; code < DHO_END; code++) { 162d6a67f0fSkrw if (!options[code].data || code == DHO_DHCP_MESSAGE_TYPE) 1639a2590e5Sderaadt continue; 1649a2590e5Sderaadt 165d7d9bbf5Skrw length = options[code].len; 16696978980Skrw if (bufix + length + 2*((length+254)/255) >= buflen) 16796978980Skrw return (lastopt); 1689a2590e5Sderaadt 16996978980Skrw lastopt = bufix; 1709a2590e5Sderaadt ix = 0; 1719a2590e5Sderaadt 1729a2590e5Sderaadt while (length) { 17396978980Skrw incr = length > 255 ? 255 : length; 1749a2590e5Sderaadt 17596978980Skrw buf[bufix++] = code; 17696978980Skrw buf[bufix++] = incr; 17796978980Skrw memcpy(buf + bufix, options[code].data + ix, incr); 1789a2590e5Sderaadt 1799a2590e5Sderaadt length -= incr; 1809a2590e5Sderaadt ix += incr; 1816fc9f4f6Skrw bufix += incr; 1829a2590e5Sderaadt } 1839a2590e5Sderaadt } 18496978980Skrw 18596978980Skrw if (bufix < buflen) { 18696978980Skrw buf[bufix] = DHO_END; 18796978980Skrw lastopt = bufix; 18896978980Skrw } 18996978980Skrw 19096978980Skrw return (lastopt); 1919a2590e5Sderaadt } 1929a2590e5Sderaadt 193c714dadcShenning /* 194c714dadcShenning * Format the specified option so that a human can easily read it. 195c714dadcShenning */ 196c714dadcShenning char * 197acf4c28bSkrw pretty_print_option(unsigned int code, struct option_data *option, 198acf4c28bSkrw int emit_punct) 1999a2590e5Sderaadt { 2009a2590e5Sderaadt static char optbuf[32768]; /* XXX */ 201285f06efSderaadt int hunksize = 0, numhunk = -1, numelem = 0; 202*8d2bd14bSkrw char fmtbuf[32], visbuf[5], *op = optbuf; 203285f06efSderaadt int i, j, k, opleft = sizeof(optbuf); 204acf4c28bSkrw unsigned char *data = option->data; 2059a2590e5Sderaadt unsigned char *dp = data; 206acf4c28bSkrw int len = option->len; 207f3a8c5fdSkrw int opcount = 0; 2089a2590e5Sderaadt struct in_addr foo; 2099a2590e5Sderaadt char comma; 2109a2590e5Sderaadt 2112f18daabSkrw memset(optbuf, 0, sizeof(optbuf)); 2122f18daabSkrw 2139a2590e5Sderaadt /* Code should be between 0 and 255. */ 2142f18daabSkrw if (code > 255) { 2152f18daabSkrw warning("pretty_print_option: bad code %d", code); 2162f18daabSkrw goto done; 2172f18daabSkrw } 2189a2590e5Sderaadt 219acf4c28bSkrw if (emit_punct) 2209a2590e5Sderaadt comma = ','; 2219a2590e5Sderaadt else 2229a2590e5Sderaadt comma = ' '; 2239a2590e5Sderaadt 2249a2590e5Sderaadt /* Figure out the size of the data. */ 2259a2590e5Sderaadt for (i = 0; dhcp_options[code].format[i]; i++) { 2269a2590e5Sderaadt if (!numhunk) { 227c955dd46Smickey warning("%s: Excess information in format string: %s", 2289a2590e5Sderaadt dhcp_options[code].name, 2299a2590e5Sderaadt &(dhcp_options[code].format[i])); 2302f18daabSkrw goto done; 2319a2590e5Sderaadt } 2329a2590e5Sderaadt numelem++; 2339a2590e5Sderaadt fmtbuf[i] = dhcp_options[code].format[i]; 2349a2590e5Sderaadt switch (dhcp_options[code].format[i]) { 2359a2590e5Sderaadt case 'A': 2369a2590e5Sderaadt --numelem; 2379a2590e5Sderaadt fmtbuf[i] = 0; 2389a2590e5Sderaadt numhunk = 0; 23929432cd9Sphessler if (hunksize == 0) { 24029432cd9Sphessler warning("%s: no size indicator before A" 24129432cd9Sphessler " in format string: %s", 24229432cd9Sphessler dhcp_options[code].name, 24329432cd9Sphessler dhcp_options[code].format); 2442f18daabSkrw goto done; 24529432cd9Sphessler } 2469a2590e5Sderaadt break; 2479a2590e5Sderaadt case 'X': 248c714dadcShenning for (k = 0; k < len; k++) 2499a2590e5Sderaadt if (!isascii(data[k]) || 2509a2590e5Sderaadt !isprint(data[k])) 2519a2590e5Sderaadt break; 252b54c879eShenning if (k == len) { 2539a2590e5Sderaadt fmtbuf[i] = 't'; 2549a2590e5Sderaadt numhunk = -2; 2559a2590e5Sderaadt } else { 2569a2590e5Sderaadt fmtbuf[i] = 'x'; 2579a2590e5Sderaadt hunksize++; 2589a2590e5Sderaadt comma = ':'; 2599a2590e5Sderaadt numhunk = 0; 2609a2590e5Sderaadt } 2619a2590e5Sderaadt fmtbuf[i + 1] = 0; 2629a2590e5Sderaadt break; 2639a2590e5Sderaadt case 't': 2649a2590e5Sderaadt fmtbuf[i] = 't'; 2659a2590e5Sderaadt fmtbuf[i + 1] = 0; 2669a2590e5Sderaadt numhunk = -2; 2679a2590e5Sderaadt break; 2689a2590e5Sderaadt case 'I': 2699a2590e5Sderaadt case 'l': 2709a2590e5Sderaadt case 'L': 2719a2590e5Sderaadt hunksize += 4; 2729a2590e5Sderaadt break; 2739a2590e5Sderaadt case 's': 2749a2590e5Sderaadt case 'S': 2759a2590e5Sderaadt hunksize += 2; 2769a2590e5Sderaadt break; 2779a2590e5Sderaadt case 'b': 2789a2590e5Sderaadt case 'B': 2799a2590e5Sderaadt case 'f': 2809a2590e5Sderaadt hunksize++; 2819a2590e5Sderaadt break; 2829a2590e5Sderaadt case 'e': 2839a2590e5Sderaadt break; 2849a2590e5Sderaadt default: 285c955dd46Smickey warning("%s: garbage in format string: %s", 2869a2590e5Sderaadt dhcp_options[code].name, 2879a2590e5Sderaadt &(dhcp_options[code].format[i])); 2882f18daabSkrw goto done; 2899a2590e5Sderaadt } 2909a2590e5Sderaadt } 2919a2590e5Sderaadt 292d22f105fSkrw /* Check for too few bytes. */ 2939a2590e5Sderaadt if (hunksize > len) { 294c955dd46Smickey warning("%s: expecting at least %d bytes; got %d", 295c714dadcShenning dhcp_options[code].name, hunksize, len); 2962f18daabSkrw goto done; 2979a2590e5Sderaadt } 298d22f105fSkrw /* Check for too many bytes. */ 2992f18daabSkrw if (numhunk == -1 && hunksize < len) { 30028f2359aSkrw warning("%s: expecting only %d bytes: got %d", 30128f2359aSkrw dhcp_options[code].name, hunksize, len); 3022f18daabSkrw goto done; 3032f18daabSkrw } 3049a2590e5Sderaadt 3059a2590e5Sderaadt /* If this is an array, compute its size. */ 3069a2590e5Sderaadt if (!numhunk) 3079a2590e5Sderaadt numhunk = len / hunksize; 3089a2590e5Sderaadt /* See if we got an exact number of hunks. */ 3092f18daabSkrw if (numhunk > 0 && numhunk * hunksize != len) { 3102f18daabSkrw warning("%s: expecting %d bytes: got %d", 3112f18daabSkrw dhcp_options[code].name, numhunk * hunksize, len); 3122f18daabSkrw goto done; 3132f18daabSkrw } 3149a2590e5Sderaadt 3159a2590e5Sderaadt /* A one-hunk array prints the same as a single hunk. */ 3169a2590e5Sderaadt if (numhunk < 0) 3179a2590e5Sderaadt numhunk = 1; 3189a2590e5Sderaadt 3199a2590e5Sderaadt /* Cycle through the array (or hunk) printing the data. */ 3209a2590e5Sderaadt for (i = 0; i < numhunk; i++) { 3219a2590e5Sderaadt for (j = 0; j < numelem; j++) { 3229a2590e5Sderaadt switch (fmtbuf[j]) { 3239a2590e5Sderaadt case 't': 324acf4c28bSkrw if (emit_punct) { 325f3a8c5fdSkrw opcount = snprintf(op, opleft, "\""); 326f3a8c5fdSkrw if (opcount >= opleft || opcount == -1) 327f3a8c5fdSkrw goto toobig; 328f3a8c5fdSkrw opleft -= opcount; 329f3a8c5fdSkrw op += opcount; 3309a2590e5Sderaadt } 3319a2590e5Sderaadt for (; dp < data + len; dp++) { 332*8d2bd14bSkrw if (*dp && strchr("\"'$`\\", *dp)) 333f3a8c5fdSkrw opcount = snprintf(op, opleft, 334f3a8c5fdSkrw "\\%c", *dp); 335*8d2bd14bSkrw else { 336*8d2bd14bSkrw vis(visbuf, *dp, VIS_OCTAL, 337*8d2bd14bSkrw *dp+1); 338f3a8c5fdSkrw opcount = snprintf(op, opleft, 339*8d2bd14bSkrw "%s", visbuf); 3409a2590e5Sderaadt } 341f3a8c5fdSkrw if (opcount >= opleft || opcount == -1) 342f3a8c5fdSkrw goto toobig; 343f3a8c5fdSkrw opleft -= opcount; 344f3a8c5fdSkrw op += opcount; 3459a2590e5Sderaadt } 346acf4c28bSkrw if (emit_punct) { 347f3a8c5fdSkrw opcount = snprintf(op, opleft, "\""); 348f3a8c5fdSkrw if (opcount >= opleft || opcount == -1) 349f3a8c5fdSkrw goto toobig; 350f3a8c5fdSkrw opleft -= opcount; 351f3a8c5fdSkrw op += opcount; 3529a2590e5Sderaadt } 353f3a8c5fdSkrw opcount = 0; /* Already moved dp & op. */ 3549a2590e5Sderaadt break; 3559a2590e5Sderaadt case 'I': 3569a2590e5Sderaadt foo.s_addr = htonl(getULong(dp)); 357f3a8c5fdSkrw opcount = snprintf(op, opleft, "%s", 358f3a8c5fdSkrw inet_ntoa(foo)); 3599a2590e5Sderaadt dp += 4; 3609a2590e5Sderaadt break; 3619a2590e5Sderaadt case 'l': 3629a2590e5Sderaadt opcount = snprintf(op, opleft, "%ld", 3639a2590e5Sderaadt (long)getLong(dp)); 3649a2590e5Sderaadt dp += 4; 3659a2590e5Sderaadt break; 3669a2590e5Sderaadt case 'L': 3679a2590e5Sderaadt opcount = snprintf(op, opleft, "%ld", 3689a2590e5Sderaadt (unsigned long)getULong(dp)); 3699a2590e5Sderaadt dp += 4; 3709a2590e5Sderaadt break; 3719a2590e5Sderaadt case 's': 3729a2590e5Sderaadt opcount = snprintf(op, opleft, "%d", 3739a2590e5Sderaadt getShort(dp)); 3749a2590e5Sderaadt dp += 2; 3759a2590e5Sderaadt break; 3769a2590e5Sderaadt case 'S': 3779a2590e5Sderaadt opcount = snprintf(op, opleft, "%d", 3789a2590e5Sderaadt getUShort(dp)); 3799a2590e5Sderaadt dp += 2; 3809a2590e5Sderaadt break; 3819a2590e5Sderaadt case 'b': 3829a2590e5Sderaadt opcount = snprintf(op, opleft, "%d", 383de3ca9dbSkrw *(char *)dp); 384de3ca9dbSkrw dp++; 3859a2590e5Sderaadt break; 3869a2590e5Sderaadt case 'B': 387de3ca9dbSkrw opcount = snprintf(op, opleft, "%d", *dp); 388de3ca9dbSkrw dp++; 3899a2590e5Sderaadt break; 3909a2590e5Sderaadt case 'x': 391de3ca9dbSkrw opcount = snprintf(op, opleft, "%x", *dp); 392de3ca9dbSkrw dp++; 3939a2590e5Sderaadt break; 3949a2590e5Sderaadt case 'f': 395f3a8c5fdSkrw opcount = snprintf(op, opleft, "%s", 396f3a8c5fdSkrw *dp ? "true" : "false"); 397de3ca9dbSkrw dp++; 3989a2590e5Sderaadt break; 3999a2590e5Sderaadt default: 400c955dd46Smickey warning("Unexpected format code %c", fmtbuf[j]); 4019a2590e5Sderaadt goto toobig; 402f3a8c5fdSkrw } 403f3a8c5fdSkrw if (opcount >= opleft || opcount == -1) 404f3a8c5fdSkrw goto toobig; 405f3a8c5fdSkrw opleft -= opcount; 406f3a8c5fdSkrw op += opcount; 4079a2590e5Sderaadt if (j + 1 < numelem && comma != ':') { 408f3a8c5fdSkrw opcount = snprintf(op, opleft, " "); 409f3a8c5fdSkrw if (opcount >= opleft || opcount == -1) 410f3a8c5fdSkrw goto toobig; 411f3a8c5fdSkrw opleft -= opcount; 412f3a8c5fdSkrw op += opcount; 4139a2590e5Sderaadt } 4149a2590e5Sderaadt } 4159a2590e5Sderaadt if (i + 1 < numhunk) { 416f3a8c5fdSkrw opcount = snprintf(op, opleft, "%c", comma); 417f3a8c5fdSkrw if (opcount >= opleft || opcount == -1) 4189a2590e5Sderaadt goto toobig; 419f3a8c5fdSkrw opleft -= opcount; 420f3a8c5fdSkrw op += opcount; 421f3a8c5fdSkrw } 4229a2590e5Sderaadt } 4232f18daabSkrw 4242f18daabSkrw done: 425c714dadcShenning return (optbuf); 4262f18daabSkrw 4279a2590e5Sderaadt toobig: 4282f18daabSkrw memset(optbuf, 0, sizeof(optbuf)); 4292f18daabSkrw return (optbuf); 4309a2590e5Sderaadt } 4319a2590e5Sderaadt 432c714dadcShenning void 433917d8addSkrw do_packet(unsigned int from_port, struct in_addr from, 434393831bbSkrw struct ether_addr *hfrom) 4359a2590e5Sderaadt { 43602e02bd5Skrw struct dhcp_packet *packet = &client->packet; 4374f062ee3Skrw struct option_data options[256]; 438b21b72f8Skrw struct reject_elem *ap; 4396896c986Skrw void (*handler)(struct in_addr, struct option_data *, char *); 4406896c986Skrw char *type, *info; 4416896c986Skrw int i, rslt, options_valid = 1; 4429a2590e5Sderaadt 443393831bbSkrw if (packet->hlen != ETHER_ADDR_LEN) { 444aff84b99Skrw #ifdef DEBUG 445aff84b99Skrw debug("Discarding packet with hlen != %s (%u)", 446aff84b99Skrw ifi->name, packet->hlen); 447aff84b99Skrw #endif 448aff84b99Skrw return; 449393831bbSkrw } else if (memcmp(&ifi->hw_address, packet->chaddr, 450393831bbSkrw sizeof(ifi->hw_address))) { 451aff84b99Skrw #ifdef DEBUG 452aff84b99Skrw debug("Discarding packet with chaddr != %s (%s)", ifi->name, 453aff84b99Skrw ether_ntoa((struct ether_addr *)packet->chaddr)); 454aff84b99Skrw #endif 4559a2590e5Sderaadt return; 4569a2590e5Sderaadt } 4579a2590e5Sderaadt 458aff84b99Skrw if (client->xid != client->packet.xid) { 459aff84b99Skrw #ifdef DEBUG 460aff84b99Skrw debug("Discarding packet with XID != %u (%u)", client->xid, 461aff84b99Skrw client->packet.xid); 462aff84b99Skrw #endif 46302e02bd5Skrw return; 464aff84b99Skrw } 465aff84b99Skrw 466aff84b99Skrw for (ap = config->reject_list; ap; ap = ap->next) 467aff84b99Skrw if (from.s_addr == ap->addr.s_addr) { 468aff84b99Skrw #ifdef DEBUG 469aff84b99Skrw debug("Discarding packet from address on reject list " 470aff84b99Skrw "(%s)", inet_ntoa(from)); 471aff84b99Skrw #endif 472aff84b99Skrw return; 473aff84b99Skrw } 4749a2590e5Sderaadt 47502e02bd5Skrw memset(options, 0, sizeof(options)); 47602e02bd5Skrw 47702e02bd5Skrw if (memcmp(&packet->options, DHCP_OPTIONS_COOKIE, 4) == 0) { 47802e02bd5Skrw /* Parse the BOOTP/DHCP options field. */ 47902e02bd5Skrw options_valid = parse_option_buffer(options, 48002e02bd5Skrw &packet->options[4], sizeof(packet->options) - 4); 48102e02bd5Skrw 48202e02bd5Skrw /* Only DHCP packets have overload areas for options. */ 48302e02bd5Skrw if (options_valid && 48402e02bd5Skrw options[DHO_DHCP_MESSAGE_TYPE].data && 48502e02bd5Skrw options[DHO_DHCP_OPTION_OVERLOAD].data) { 48602e02bd5Skrw if (options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 1) 48702e02bd5Skrw options_valid = parse_option_buffer(options, 48802e02bd5Skrw (unsigned char *)packet->file, 48902e02bd5Skrw sizeof(packet->file)); 49002e02bd5Skrw if (options_valid && 49102e02bd5Skrw options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 2) 49202e02bd5Skrw options_valid = parse_option_buffer(options, 49302e02bd5Skrw (unsigned char *)packet->sname, 49402e02bd5Skrw sizeof(packet->sname)); 49502e02bd5Skrw } 49602e02bd5Skrw } 49702e02bd5Skrw 4986896c986Skrw type = "<unknown>"; 49902e02bd5Skrw handler = NULL; 50002e02bd5Skrw 5014f062ee3Skrw if (options[DHO_DHCP_MESSAGE_TYPE].data) { 50202e02bd5Skrw /* Always try a DHCP packet, even if a bad option was seen. */ 50302e02bd5Skrw switch (options[DHO_DHCP_MESSAGE_TYPE].data[0]) { 50402e02bd5Skrw case DHCPOFFER: 50502e02bd5Skrw handler = dhcpoffer; 50602e02bd5Skrw type = "DHCPOFFER"; 50702e02bd5Skrw break; 50802e02bd5Skrw case DHCPNAK: 50902e02bd5Skrw handler = dhcpnak; 51002e02bd5Skrw type = "DHCPNACK"; 51102e02bd5Skrw break; 51202e02bd5Skrw case DHCPACK: 51302e02bd5Skrw handler = dhcpack; 51402e02bd5Skrw type = "DHCPACK"; 51502e02bd5Skrw break; 51602e02bd5Skrw default: 517aff84b99Skrw #ifdef DEBUG 518aff84b99Skrw debug("Discarding DHCP packet of unknown type (%d)", 519aff84b99Skrw options[DHO_DHCP_MESSAGE_TYPE].data[0]); 520aff84b99Skrw #endif 52102e02bd5Skrw break; 52202e02bd5Skrw } 52302e02bd5Skrw } else if (options_valid && packet->op == BOOTREPLY) { 52402e02bd5Skrw handler = dhcpoffer; 52502e02bd5Skrw type = "BOOTREPLY"; 526aff84b99Skrw } else { 527aff84b99Skrw #ifdef DEBUG 528aff84b99Skrw debug("Discarding packet which is neither DHCP nor BOOTP"); 529aff84b99Skrw #endif 53002e02bd5Skrw } 5319a2590e5Sderaadt 5326896c986Skrw rslt = asprintf(&info, "%s from %s (%s)", type, inet_ntoa(from), 533393831bbSkrw ether_ntoa(hfrom)); 5346896c986Skrw if (rslt == -1) 5356896c986Skrw error("no memory for info string"); 5366896c986Skrw 53702e02bd5Skrw if (handler) 5386896c986Skrw (*handler)(from, options, info); 5396896c986Skrw 5406896c986Skrw free(info); 54102e02bd5Skrw 542c714dadcShenning for (i = 0; i < 256; i++) 5434f062ee3Skrw if (options[i].len && options[i].data) 5444f062ee3Skrw free(options[i].data); 5459a2590e5Sderaadt } 546