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]
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>
37 #endif /* _KERNEL && !_BOOT */
39 static uint8_t bootmagic
[] = BOOTMAGIC
;
42 * Scan field for options.
45 field_scan(uint8_t *start
, uint8_t *end
, DHCP_OPT
**options
,
51 if (*start
== CD_PAD
) {
57 if (*start
> last_option
) {
60 continue; /* unrecognized option */
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
)
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
;
86 * Scan Vendor field for options.
89 vendor_scan(PKT_LIST
*pl
)
91 uint8_t *start
, *end
, len
;
93 if (pl
->opts
[CD_VENDOR_SPEC
] == NULL
)
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
)
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
)
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) {
128 /* check the options field */
129 field_scan(pkt
->options
, &pkt
->options
[opt_size
], pl
->opts
,
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
))
140 if (pl
->opts
[CD_DHCP_TYPE
] == NULL
)
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
) {
157 field_scan(pkt
->file
, &pkt
->cookie
[0], pl
->opts
,
161 field_scan(pkt
->sname
, &pkt
->file
[0], pl
->opts
,
165 field_scan(pkt
->file
, &pkt
->cookie
[0], pl
->opts
,
167 field_scan(pkt
->sname
, &pkt
->file
[0], pl
->opts
,
171 pl
->opts
[CD_OPTION_OVERLOAD
] = NULL
;
172 return (DHCP_BAD_OPT_OVLD
);
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.
193 dhcpv6_find_option(const void *buffer
, size_t buflen
,
194 const dhcpv6_option_t
*oldopt
, uint16_t codenum
, uint_t
*retlenp
)
200 codenum
= htons(codenum
);
202 while (buflen
>= sizeof (dhcpv6_option_t
)) {
203 (void) memcpy(&d6o
, bp
, sizeof (d6o
));
204 olen
= ntohs(d6o
.d6o_len
) + sizeof (d6o
);
207 if (d6o
.d6o_code
!= codenum
||
208 (oldopt
!= NULL
&& bp
<= (const uchar_t
*)oldopt
)) {
215 /* LINTED: alignment */
216 return ((dhcpv6_option_t
*)bp
);
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.
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
))
236 d6m
= (const dhcpv6_message_t
*)plp
->pkt
;
237 return (dhcpv6_find_option(d6m
+ 1, plp
->len
- sizeof (*d6m
), oldopt
,