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