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