xref: /illumos-gate/usr/src/uts/common/io/mac/mac_protect.c (revision dbed73cbda2229fd1aa6dc5743993cae7f0a7ee9)
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 (the "License").
6  * You may not use this file except in compliance with the License.
7  *
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.
12  *
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]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <sys/strsun.h>
28 #include <sys/sdt.h>
29 #include <sys/mac.h>
30 #include <sys/mac_impl.h>
31 #include <sys/mac_client_impl.h>
32 #include <sys/mac_client_priv.h>
33 #include <sys/ethernet.h>
34 #include <sys/vlan.h>
35 #include <sys/dlpi.h>
36 #include <inet/ip.h>
37 #include <inet/ip6.h>
38 #include <inet/arp.h>
39 
40 /*
41  * Check if ipaddr is in the 'allowed-ips' list.
42  */
43 static boolean_t
44 ipnospoof_check_ips(mac_protect_t *protect, ipaddr_t ipaddr)
45 {
46 	uint_t i;
47 
48 	/*
49 	 * unspecified addresses are harmless and are used by ARP,DHCP..etc.
50 	 */
51 	if (ipaddr == INADDR_ANY)
52 		return (B_TRUE);
53 
54 	for (i = 0; i < protect->mp_ipaddrcnt; i++) {
55 		if (protect->mp_ipaddrs[i] == ipaddr)
56 			return (B_TRUE);
57 	}
58 	return (B_FALSE);
59 }
60 
61 /*
62  * Enforce ip-nospoof protection. Only IPv4 is supported for now.
63  */
64 static int
65 ipnospoof_check(mac_client_impl_t *mcip, mac_protect_t *protect,
66     mblk_t *mp, mac_header_info_t *mhip)
67 {
68 	uint32_t	sap = mhip->mhi_bindsap;
69 	uchar_t		*start = mp->b_rptr + mhip->mhi_hdrsize;
70 	int		err = EINVAL;
71 
72 	/*
73 	 * This handles the case where the mac header is not in
74 	 * the same mblk as the IP header.
75 	 */
76 	if (start == mp->b_wptr) {
77 		mp = mp->b_cont;
78 
79 		/*
80 		 * IP header missing. Let the packet through.
81 		 */
82 		if (mp == NULL)
83 			return (0);
84 
85 		start = mp->b_rptr;
86 	}
87 
88 	switch (sap) {
89 	case ETHERTYPE_IP: {
90 		ipha_t	*ipha = (ipha_t *)start;
91 
92 		if (start + sizeof (ipha_t) > mp->b_wptr || !OK_32PTR(start))
93 			goto fail;
94 
95 		if (!ipnospoof_check_ips(protect, ipha->ipha_src))
96 			goto fail;
97 
98 		break;
99 	}
100 	case ETHERTYPE_ARP: {
101 		arh_t		*arh = (arh_t *)start;
102 		uint32_t	maclen, hlen, plen, arplen;
103 		ipaddr_t	spaddr;
104 		uchar_t		*shaddr;
105 
106 		if (start + sizeof (arh_t) > mp->b_wptr)
107 			goto fail;
108 
109 		maclen = mcip->mci_mip->mi_info.mi_addr_length;
110 		hlen = arh->arh_hlen;
111 		plen = arh->arh_plen;
112 		if ((hlen != 0 && hlen != maclen) ||
113 		    plen != sizeof (ipaddr_t))
114 			goto fail;
115 
116 		arplen = sizeof (arh_t) + 2 * hlen + 2 * plen;
117 		if (start + arplen > mp->b_wptr)
118 			goto fail;
119 
120 		shaddr = start + sizeof (arh_t);
121 		if (hlen != 0 &&
122 		    bcmp(mcip->mci_unicast->ma_addr, shaddr, maclen) != 0)
123 			goto fail;
124 
125 		bcopy(shaddr + hlen, &spaddr, sizeof (spaddr));
126 		if (!ipnospoof_check_ips(protect, spaddr))
127 			goto fail;
128 		break;
129 	}
130 	default:
131 		break;
132 	}
133 	return (0);
134 
135 fail:
136 	/* increment ipnospoof stat here */
137 	return (err);
138 }
139 
140 /*
141  * Enforce link protection on one packet.
142  */
143 static int
144 mac_protect_check_one(mac_client_impl_t *mcip, mblk_t *mp)
145 {
146 	mac_impl_t		*mip = mcip->mci_mip;
147 	mac_resource_props_t	*mrp = MCIP_RESOURCE_PROPS(mcip);
148 	mac_protect_t		*protect;
149 	mac_header_info_t	mhi;
150 	uint32_t		types;
151 	int			err;
152 
153 	ASSERT(mp->b_next == NULL);
154 	ASSERT(mrp != NULL);
155 
156 	err = mac_vlan_header_info((mac_handle_t)mip, mp, &mhi);
157 	if (err != 0) {
158 		DTRACE_PROBE2(invalid__header, mac_client_impl_t *, mcip,
159 		    mblk_t *, mp);
160 		return (err);
161 	}
162 
163 	protect = &mrp->mrp_protect;
164 	types = protect->mp_types;
165 
166 	if ((types & MPT_MACNOSPOOF) != 0) {
167 		if (mhi.mhi_saddr != NULL &&
168 		    bcmp(mcip->mci_unicast->ma_addr, mhi.mhi_saddr,
169 		    mip->mi_info.mi_addr_length) != 0) {
170 			DTRACE_PROBE2(mac__nospoof__fail,
171 			    mac_client_impl_t *, mcip, mblk_t *, mp);
172 			return (EINVAL);
173 		}
174 	}
175 
176 	if ((types & MPT_RESTRICTED) != 0) {
177 		uint32_t	vid = VLAN_ID(mhi.mhi_tci);
178 		uint32_t	sap = mhi.mhi_bindsap;
179 
180 		/*
181 		 * ETHERTYPE_VLAN packets are allowed through, provided that
182 		 * the vid is not spoofed.
183 		 */
184 		if (vid != 0 && !mac_client_check_flow_vid(mcip, vid)) {
185 			DTRACE_PROBE2(restricted__vid__invalid,
186 			    mac_client_impl_t *, mcip, mblk_t *, mp);
187 			return (EINVAL);
188 		}
189 
190 		if (sap != ETHERTYPE_IP && sap != ETHERTYPE_IPV6 &&
191 		    sap != ETHERTYPE_ARP) {
192 			DTRACE_PROBE2(restricted__fail,
193 			    mac_client_impl_t *, mcip, mblk_t *, mp);
194 			return (EINVAL);
195 		}
196 	}
197 
198 	if ((types & MPT_IPNOSPOOF) != 0) {
199 		if ((err = ipnospoof_check(mcip, protect,
200 		    mp, &mhi)) != 0) {
201 			DTRACE_PROBE2(ip__nospoof__fail,
202 			    mac_client_impl_t *, mcip, mblk_t *, mp);
203 			return (err);
204 		}
205 	}
206 	return (0);
207 }
208 
209 /*
210  * Enforce link protection on a packet chain.
211  * Packets that pass the checks are returned back to the caller.
212  */
213 mblk_t *
214 mac_protect_check(mac_client_handle_t mch, mblk_t *mp)
215 {
216 	mac_client_impl_t	*mcip = (mac_client_impl_t *)mch;
217 	mblk_t			*ret_mp = NULL, **tailp = &ret_mp, *next;
218 
219 	/*
220 	 * Skip checks if we are part of an aggr.
221 	 */
222 	if ((mcip->mci_state_flags & MCIS_IS_AGGR_PORT) != 0)
223 		return (mp);
224 
225 	for (; mp != NULL; mp = next) {
226 		next = mp->b_next;
227 		mp->b_next = NULL;
228 
229 		if (mac_protect_check_one(mcip, mp) == 0) {
230 			*tailp = mp;
231 			tailp = &mp->b_next;
232 		} else {
233 			freemsg(mp);
234 		}
235 	}
236 	return (ret_mp);
237 }
238 
239 /*
240  * Check if a particular protection type is enabled.
241  */
242 boolean_t
243 mac_protect_enabled(mac_client_handle_t mch, uint32_t type)
244 {
245 	mac_client_impl_t	*mcip = (mac_client_impl_t *)mch;
246 	mac_resource_props_t	*mrp = MCIP_RESOURCE_PROPS(mcip);
247 
248 	ASSERT(mrp != NULL);
249 	return ((mrp->mrp_protect.mp_types & type) != 0);
250 }
251 
252 /*
253  * Sanity-checks parameters given by userland.
254  */
255 int
256 mac_protect_validate(mac_resource_props_t *mrp)
257 {
258 	mac_protect_t	*p = &mrp->mrp_protect;
259 
260 	/* check for invalid types */
261 	if (p->mp_types != MPT_RESET && (p->mp_types & ~MPT_ALL) != 0)
262 		return (EINVAL);
263 
264 	if (p->mp_ipaddrcnt != MPT_RESET) {
265 		uint_t	i, j;
266 
267 		if (p->mp_ipaddrcnt > MPT_MAXIPADDR)
268 			return (EINVAL);
269 
270 		for (i = 0; i < p->mp_ipaddrcnt; i++) {
271 			/*
272 			 * The unspecified address is implicitly allowed
273 			 * so there's no need to add it to the list.
274 			 */
275 			if (p->mp_ipaddrs[i] == INADDR_ANY)
276 				return (EINVAL);
277 
278 			for (j = 0; j < p->mp_ipaddrcnt; j++) {
279 				/* found a duplicate */
280 				if (i != j &&
281 				    p->mp_ipaddrs[i] == p->mp_ipaddrs[j])
282 					return (EINVAL);
283 			}
284 		}
285 	}
286 	return (0);
287 }
288 
289 /*
290  * Enable/disable link protection.
291  */
292 int
293 mac_protect_set(mac_client_handle_t mch, mac_resource_props_t *mrp)
294 {
295 	mac_client_impl_t	*mcip = (mac_client_impl_t *)mch;
296 	mac_impl_t		*mip = mcip->mci_mip;
297 	uint_t			media = mip->mi_info.mi_nativemedia;
298 	int			err;
299 
300 	ASSERT(MAC_PERIM_HELD((mac_handle_t)mip));
301 
302 	/* tunnels are not supported */
303 	if (media == DL_IPV4 || media == DL_IPV6 || media == DL_6TO4)
304 		return (ENOTSUP);
305 
306 	if ((err = mac_protect_validate(mrp)) != 0)
307 		return (err);
308 
309 	mac_update_resources(mrp, MCIP_RESOURCE_PROPS(mcip), B_FALSE);
310 	return (0);
311 }
312 
313 void
314 mac_protect_update(mac_resource_props_t *new, mac_resource_props_t *curr)
315 {
316 	mac_protect_t	*np = &new->mrp_protect;
317 	mac_protect_t	*cp = &curr->mrp_protect;
318 	uint32_t	types = np->mp_types;
319 
320 	if (types == MPT_RESET) {
321 		cp->mp_types = 0;
322 		curr->mrp_mask &= ~MRP_PROTECT;
323 	} else {
324 		if (types != 0) {
325 			cp->mp_types = types;
326 			curr->mrp_mask |= MRP_PROTECT;
327 		}
328 	}
329 
330 	if (np->mp_ipaddrcnt != 0) {
331 		if (np->mp_ipaddrcnt < MPT_MAXIPADDR) {
332 			bcopy(np->mp_ipaddrs, cp->mp_ipaddrs,
333 			    sizeof (cp->mp_ipaddrs));
334 			cp->mp_ipaddrcnt = np->mp_ipaddrcnt;
335 		} else if (np->mp_ipaddrcnt == MPT_RESET) {
336 			bzero(cp->mp_ipaddrs, sizeof (cp->mp_ipaddrs));
337 			cp->mp_ipaddrcnt = 0;
338 		}
339 	}
340 }
341