xref: /illumos-gate/usr/src/common/net/dhcp/scan.c (revision f63f7506be0210195779706f51c58646e568cc40)
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