8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / common / net / dhcp / scan.c
blob9bd4eccbdea61b9c3636088e9e28210484872b0c
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
25 * Routines used to extract/insert DHCP options. Must be kept MT SAFE,
26 * as they are called from different threads.
29 #pragma ident "%Z%%M% %I% %E% SMI"
31 #include <sys/types.h>
32 #include "dhcp_impl.h"
33 #if defined(_KERNEL) && !defined(_BOOT)
34 #include <sys/sunddi.h>
35 #else
36 #include <strings.h>
37 #endif /* _KERNEL && !_BOOT */
39 static uint8_t bootmagic[] = BOOTMAGIC;
42 * Scan field for options.
44 static void
45 field_scan(uint8_t *start, uint8_t *end, DHCP_OPT **options,
46 uint8_t last_option)
48 uint8_t *current;
50 while (start < end) {
51 if (*start == CD_PAD) {
52 start++;
53 continue;
55 if (*start == CD_END)
56 break; /* done */
57 if (*start > last_option) {
58 if (++start < end)
59 start += *start + 1;
60 continue; /* unrecognized option */
63 current = start;
64 if (++start < end)
65 start += *start + 1; /* advance to next option */
67 /* all options besides CD_END and CD_PAD should have a len */
68 if ((current + 1) >= end)
69 continue;
71 /* Ignores duplicate options. */
72 if (options[*current] == NULL) {
74 options[*current] = (DHCP_OPT *)current;
76 /* verify that len won't go beyond end */
77 if ((current + options[*current]->len + 1) >= end) {
78 options[*current] = NULL;
79 continue;
86 * Scan Vendor field for options.
88 static void
89 vendor_scan(PKT_LIST *pl)
91 uint8_t *start, *end, len;
93 if (pl->opts[CD_VENDOR_SPEC] == NULL)
94 return;
95 len = pl->opts[CD_VENDOR_SPEC]->len;
96 start = pl->opts[CD_VENDOR_SPEC]->value;
98 /* verify that len won't go beyond the end of the packet */
99 if (((start - (uint8_t *)pl->pkt) + len) > pl->len)
100 return;
102 end = start + len;
103 field_scan(start, end, pl->vs, VS_OPTION_END);
107 * Load opts table in PKT_LIST entry with PKT's options.
108 * Returns 0 if no fatal errors occur, otherwise...
111 dhcp_options_scan(PKT_LIST *pl, boolean_t scan_vendor)
113 PKT *pkt = pl->pkt;
114 uint_t opt_size = pl->len - BASE_PKT_SIZE;
117 * bcmp() is used here instead of memcmp() since kernel/standalone
118 * doesn't have a memcmp().
120 if (pl->len < BASE_PKT_SIZE ||
121 bcmp(pl->pkt->cookie, bootmagic, sizeof (pl->pkt->cookie)) != 0) {
122 pl->rfc1048 = 0;
123 return (0);
126 pl->rfc1048 = 1;
128 /* check the options field */
129 field_scan(pkt->options, &pkt->options[opt_size], pl->opts,
130 DHCP_LAST_OPT);
133 * process vendor specific options. We look at the vendor options
134 * here, simply because a BOOTP server could fake DHCP vendor
135 * options. This increases our interoperability with BOOTP.
137 if (scan_vendor && (pl->opts[CD_VENDOR_SPEC] != NULL))
138 vendor_scan(pl);
140 if (pl->opts[CD_DHCP_TYPE] == NULL)
141 return (0);
143 if (pl->opts[CD_DHCP_TYPE]->len != 1)
144 return (DHCP_GARBLED_MSG_TYPE);
146 if (*pl->opts[CD_DHCP_TYPE]->value < DISCOVER ||
147 *pl->opts[CD_DHCP_TYPE]->value > INFORM)
148 return (DHCP_WRONG_MSG_TYPE);
150 if (pl->opts[CD_OPTION_OVERLOAD]) {
151 if (pl->opts[CD_OPTION_OVERLOAD]->len != 1) {
152 pl->opts[CD_OPTION_OVERLOAD] = NULL;
153 return (DHCP_BAD_OPT_OVLD);
155 switch (*pl->opts[CD_OPTION_OVERLOAD]->value) {
156 case 1:
157 field_scan(pkt->file, &pkt->cookie[0], pl->opts,
158 DHCP_LAST_OPT);
159 break;
160 case 2:
161 field_scan(pkt->sname, &pkt->file[0], pl->opts,
162 DHCP_LAST_OPT);
163 break;
164 case 3:
165 field_scan(pkt->file, &pkt->cookie[0], pl->opts,
166 DHCP_LAST_OPT);
167 field_scan(pkt->sname, &pkt->file[0], pl->opts,
168 DHCP_LAST_OPT);
169 break;
170 default:
171 pl->opts[CD_OPTION_OVERLOAD] = NULL;
172 return (DHCP_BAD_OPT_OVLD);
175 return (0);
179 * Locate a DHCPv6 option or suboption within a buffer. DHCPv6 uses nested
180 * options within options, and this function is designed to work with both
181 * primary options and the suboptions contained within.
183 * The 'oldopt' is a previous option pointer, and is typically used to iterate
184 * over options of the same code number. The 'codenum' is in host byte order
185 * for simplicity. 'retlenp' may be NULL, and if present gets the _entire_
186 * option length (including header).
188 * Warning: the returned pointer has no particular alignment because DHCPv6
189 * defines options without alignment. The caller must deal with unaligned
190 * pointers carefully.
192 dhcpv6_option_t *
193 dhcpv6_find_option(const void *buffer, size_t buflen,
194 const dhcpv6_option_t *oldopt, uint16_t codenum, uint_t *retlenp)
196 const uchar_t *bp;
197 dhcpv6_option_t d6o;
198 uint_t olen;
200 codenum = htons(codenum);
201 bp = buffer;
202 while (buflen >= sizeof (dhcpv6_option_t)) {
203 (void) memcpy(&d6o, bp, sizeof (d6o));
204 olen = ntohs(d6o.d6o_len) + sizeof (d6o);
205 if (olen > buflen)
206 break;
207 if (d6o.d6o_code != codenum ||
208 (oldopt != NULL && bp <= (const uchar_t *)oldopt)) {
209 bp += olen;
210 buflen -= olen;
211 continue;
213 if (retlenp != NULL)
214 *retlenp = olen;
215 /* LINTED: alignment */
216 return ((dhcpv6_option_t *)bp);
218 return (NULL);
222 * Locate a DHCPv6 option within the top level of a PKT_LIST entry. DHCPv6
223 * uses nested options within options, and this function returns only the
224 * primary options. Use dhcpv6_find_option to traverse suboptions.
226 * See dhcpv6_find_option for usage details and warnings.
228 dhcpv6_option_t *
229 dhcpv6_pkt_option(const PKT_LIST *plp, const dhcpv6_option_t *oldopt,
230 uint16_t codenum, uint_t *retlenp)
232 const dhcpv6_message_t *d6m;
234 if (plp == NULL || plp->pkt == NULL || plp->len < sizeof (*d6m))
235 return (NULL);
236 d6m = (const dhcpv6_message_t *)plp->pkt;
237 return (dhcpv6_find_option(d6m + 1, plp->len - sizeof (*d6m), oldopt,
238 codenum, retlenp));