1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 1996-2001, 2003 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 * 26 * Routines used to extract/insert DHCP options. Must be kept MT SAFE, 27 * as they are called from different threads. 28 */ 29 30 #pragma ident "%Z%%M% %I% %E% SMI" 31 32 #include <sys/types.h> 33 #include "dhcp_impl.h" 34 #if defined(_KERNEL) && !defined(_BOOT) 35 #include <sys/sunddi.h> 36 #else 37 #include <strings.h> 38 #endif /* _KERNEL && !_BOOT */ 39 40 static uint8_t bootmagic[] = BOOTMAGIC; 41 42 /* 43 * Scan field for options. 44 */ 45 static void 46 field_scan(uint8_t *start, uint8_t *end, DHCP_OPT **options, 47 uint8_t last_option) 48 { 49 uint8_t *current; 50 51 while (start < end) { 52 if (*start == CD_PAD) { 53 start++; 54 continue; 55 } 56 if (*start == CD_END) 57 break; /* done */ 58 if (*start > last_option) { 59 if (++start < end) 60 start += *start + 1; 61 continue; /* unrecognized option */ 62 } 63 64 current = start; 65 if (++start < end) 66 start += *start + 1; /* advance to next option */ 67 68 /* all options besides CD_END and CD_PAD should have a len */ 69 if ((current + 1) >= end) 70 continue; 71 72 /* Ignores duplicate options. */ 73 if (options[*current] == NULL) { 74 75 options[*current] = (DHCP_OPT *)current; 76 77 /* verify that len won't go beyond end */ 78 if ((current + options[*current]->len + 1) >= end) { 79 options[*current] = NULL; 80 continue; 81 } 82 } 83 } 84 } 85 86 /* 87 * Scan Vendor field for options. 88 */ 89 static void 90 vendor_scan(PKT_LIST *pl) 91 { 92 uint8_t *start, *end, len; 93 94 if (pl->opts[CD_VENDOR_SPEC] == NULL) 95 return; 96 len = pl->opts[CD_VENDOR_SPEC]->len; 97 start = pl->opts[CD_VENDOR_SPEC]->value; 98 99 /* verify that len won't go beyond the end of the packet */ 100 if (((start - (uint8_t *)pl->pkt) + len) > pl->len) 101 return; 102 103 end = start + len; 104 field_scan(start, end, pl->vs, VS_OPTION_END); 105 } 106 107 /* 108 * Load opts table in PKT_LIST entry with PKT's options. 109 * Returns 0 if no fatal errors occur, otherwise... 110 */ 111 int 112 dhcp_options_scan(PKT_LIST *pl, boolean_t scan_vendor) 113 { 114 PKT *pkt = pl->pkt; 115 uint_t opt_size = pl->len - BASE_PKT_SIZE; 116 117 /* 118 * bcmp() is used here instead of memcmp() since kernel/standalone 119 * doesn't have a memcmp(). 120 */ 121 if (pl->len < BASE_PKT_SIZE || 122 bcmp(pl->pkt->cookie, bootmagic, sizeof (pl->pkt->cookie)) != 0) { 123 pl->rfc1048 = 0; 124 return (0); 125 } 126 127 pl->rfc1048 = 1; 128 129 /* check the options field */ 130 field_scan(pkt->options, &pkt->options[opt_size], pl->opts, 131 DHCP_LAST_OPT); 132 133 /* 134 * process vendor specific options. We look at the vendor options 135 * here, simply because a BOOTP server could fake DHCP vendor 136 * options. This increases our interoperability with BOOTP. 137 */ 138 if (scan_vendor && (pl->opts[CD_VENDOR_SPEC] != NULL)) 139 vendor_scan(pl); 140 141 if (pl->opts[CD_DHCP_TYPE] == NULL) 142 return (0); 143 144 if (pl->opts[CD_DHCP_TYPE]->len != 1) 145 return (DHCP_GARBLED_MSG_TYPE); 146 147 if (*pl->opts[CD_DHCP_TYPE]->value < DISCOVER || 148 *pl->opts[CD_DHCP_TYPE]->value > INFORM) 149 return (DHCP_WRONG_MSG_TYPE); 150 151 if (pl->opts[CD_OPTION_OVERLOAD]) { 152 if (pl->opts[CD_OPTION_OVERLOAD]->len != 1) { 153 pl->opts[CD_OPTION_OVERLOAD] = NULL; 154 return (DHCP_BAD_OPT_OVLD); 155 } 156 switch (*pl->opts[CD_OPTION_OVERLOAD]->value) { 157 case 1: 158 field_scan(pkt->file, &pkt->cookie[0], pl->opts, 159 DHCP_LAST_OPT); 160 break; 161 case 2: 162 field_scan(pkt->sname, &pkt->file[0], pl->opts, 163 DHCP_LAST_OPT); 164 break; 165 case 3: 166 field_scan(pkt->file, &pkt->cookie[0], pl->opts, 167 DHCP_LAST_OPT); 168 field_scan(pkt->sname, &pkt->file[0], pl->opts, 169 DHCP_LAST_OPT); 170 break; 171 default: 172 pl->opts[CD_OPTION_OVERLOAD] = NULL; 173 return (DHCP_BAD_OPT_OVLD); 174 } 175 } 176 return (0); 177 } 178