/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include #include #include /* * Check if ipaddr is in the 'allowed-ips' list. */ static boolean_t ipnospoof_check_ips(mac_protect_t *protect, ipaddr_t ipaddr) { uint_t i; /* * unspecified addresses are harmless and are used by ARP,DHCP..etc. */ if (ipaddr == INADDR_ANY) return (B_TRUE); for (i = 0; i < protect->mp_ipaddrcnt; i++) { if (protect->mp_ipaddrs[i] == ipaddr) return (B_TRUE); } return (B_FALSE); } /* * Enforce ip-nospoof protection. Only IPv4 is supported for now. */ static int ipnospoof_check(mac_client_impl_t *mcip, mac_protect_t *protect, mblk_t *mp, mac_header_info_t *mhip) { uint32_t sap = mhip->mhi_bindsap; uchar_t *start = mp->b_rptr + mhip->mhi_hdrsize; int err = EINVAL; /* * This handles the case where the mac header is not in * the same mblk as the IP header. */ if (start == mp->b_wptr) { mp = mp->b_cont; /* * IP header missing. Let the packet through. */ if (mp == NULL) return (0); start = mp->b_rptr; } switch (sap) { case ETHERTYPE_IP: { ipha_t *ipha = (ipha_t *)start; if (start + sizeof (ipha_t) > mp->b_wptr || !OK_32PTR(start)) goto fail; if (!ipnospoof_check_ips(protect, ipha->ipha_src)) goto fail; break; } case ETHERTYPE_ARP: { arh_t *arh = (arh_t *)start; uint32_t maclen, hlen, plen, arplen; ipaddr_t spaddr; uchar_t *shaddr; if (start + sizeof (arh_t) > mp->b_wptr) goto fail; maclen = mcip->mci_mip->mi_info.mi_addr_length; hlen = arh->arh_hlen; plen = arh->arh_plen; if ((hlen != 0 && hlen != maclen) || plen != sizeof (ipaddr_t)) goto fail; arplen = sizeof (arh_t) + 2 * hlen + 2 * plen; if (start + arplen > mp->b_wptr) goto fail; shaddr = start + sizeof (arh_t); if (hlen != 0 && bcmp(mcip->mci_unicast->ma_addr, shaddr, maclen) != 0) goto fail; bcopy(shaddr + hlen, &spaddr, sizeof (spaddr)); if (!ipnospoof_check_ips(protect, spaddr)) goto fail; break; } default: break; } return (0); fail: /* increment ipnospoof stat here */ return (err); } /* * Enforce link protection on one packet. */ static int mac_protect_check_one(mac_client_impl_t *mcip, mblk_t *mp) { mac_impl_t *mip = mcip->mci_mip; mac_resource_props_t *mrp = MCIP_RESOURCE_PROPS(mcip); mac_protect_t *protect; mac_header_info_t mhi; uint32_t types; int err; ASSERT(mp->b_next == NULL); ASSERT(mrp != NULL); err = mac_vlan_header_info((mac_handle_t)mip, mp, &mhi); if (err != 0) { DTRACE_PROBE2(invalid__header, mac_client_impl_t *, mcip, mblk_t *, mp); return (err); } protect = &mrp->mrp_protect; types = protect->mp_types; if ((types & MPT_MACNOSPOOF) != 0) { if (mhi.mhi_saddr != NULL && bcmp(mcip->mci_unicast->ma_addr, mhi.mhi_saddr, mip->mi_info.mi_addr_length) != 0) { DTRACE_PROBE2(mac__nospoof__fail, mac_client_impl_t *, mcip, mblk_t *, mp); return (EINVAL); } } if ((types & MPT_RESTRICTED) != 0) { uint32_t vid = VLAN_ID(mhi.mhi_tci); uint32_t sap = mhi.mhi_bindsap; /* * ETHERTYPE_VLAN packets are allowed through, provided that * the vid is not spoofed. */ if (vid != 0 && !mac_client_check_flow_vid(mcip, vid)) { DTRACE_PROBE2(restricted__vid__invalid, mac_client_impl_t *, mcip, mblk_t *, mp); return (EINVAL); } if (sap != ETHERTYPE_IP && sap != ETHERTYPE_IPV6 && sap != ETHERTYPE_ARP) { DTRACE_PROBE2(restricted__fail, mac_client_impl_t *, mcip, mblk_t *, mp); return (EINVAL); } } if ((types & MPT_IPNOSPOOF) != 0) { if ((err = ipnospoof_check(mcip, protect, mp, &mhi)) != 0) { DTRACE_PROBE2(ip__nospoof__fail, mac_client_impl_t *, mcip, mblk_t *, mp); return (err); } } return (0); } /* * Enforce link protection on a packet chain. * Packets that pass the checks are returned back to the caller. */ mblk_t * mac_protect_check(mac_client_handle_t mch, mblk_t *mp) { mac_client_impl_t *mcip = (mac_client_impl_t *)mch; mblk_t *ret_mp = NULL, **tailp = &ret_mp, *next; /* * Skip checks if we are part of an aggr. */ if ((mcip->mci_state_flags & MCIS_IS_AGGR_PORT) != 0) return (mp); for (; mp != NULL; mp = next) { next = mp->b_next; mp->b_next = NULL; if (mac_protect_check_one(mcip, mp) == 0) { *tailp = mp; tailp = &mp->b_next; } else { freemsg(mp); } } return (ret_mp); } /* * Check if a particular protection type is enabled. */ boolean_t mac_protect_enabled(mac_client_handle_t mch, uint32_t type) { mac_client_impl_t *mcip = (mac_client_impl_t *)mch; mac_resource_props_t *mrp = MCIP_RESOURCE_PROPS(mcip); ASSERT(mrp != NULL); return ((mrp->mrp_protect.mp_types & type) != 0); } /* * Sanity-checks parameters given by userland. */ int mac_protect_validate(mac_resource_props_t *mrp) { mac_protect_t *p = &mrp->mrp_protect; /* check for invalid types */ if (p->mp_types != MPT_RESET && (p->mp_types & ~MPT_ALL) != 0) return (EINVAL); if (p->mp_ipaddrcnt != MPT_RESET) { uint_t i, j; if (p->mp_ipaddrcnt > MPT_MAXIPADDR) return (EINVAL); for (i = 0; i < p->mp_ipaddrcnt; i++) { /* * The unspecified address is implicitly allowed * so there's no need to add it to the list. */ if (p->mp_ipaddrs[i] == INADDR_ANY) return (EINVAL); for (j = 0; j < p->mp_ipaddrcnt; j++) { /* found a duplicate */ if (i != j && p->mp_ipaddrs[i] == p->mp_ipaddrs[j]) return (EINVAL); } } } return (0); } /* * Enable/disable link protection. */ int mac_protect_set(mac_client_handle_t mch, mac_resource_props_t *mrp) { mac_client_impl_t *mcip = (mac_client_impl_t *)mch; mac_impl_t *mip = mcip->mci_mip; uint_t media = mip->mi_info.mi_nativemedia; int err; ASSERT(MAC_PERIM_HELD((mac_handle_t)mip)); /* tunnels are not supported */ if (media == DL_IPV4 || media == DL_IPV6 || media == DL_6TO4) return (ENOTSUP); if ((err = mac_protect_validate(mrp)) != 0) return (err); mac_update_resources(mrp, MCIP_RESOURCE_PROPS(mcip), B_FALSE); return (0); } void mac_protect_update(mac_resource_props_t *new, mac_resource_props_t *curr) { mac_protect_t *np = &new->mrp_protect; mac_protect_t *cp = &curr->mrp_protect; uint32_t types = np->mp_types; if (types == MPT_RESET) { cp->mp_types = 0; curr->mrp_mask &= ~MRP_PROTECT; } else { if (types != 0) { cp->mp_types = types; curr->mrp_mask |= MRP_PROTECT; } } if (np->mp_ipaddrcnt != 0) { if (np->mp_ipaddrcnt < MPT_MAXIPADDR) { bcopy(np->mp_ipaddrs, cp->mp_ipaddrs, sizeof (cp->mp_ipaddrs)); cp->mp_ipaddrcnt = np->mp_ipaddrcnt; } else if (np->mp_ipaddrcnt == MPT_RESET) { bzero(cp->mp_ipaddrs, sizeof (cp->mp_ipaddrs)); cp->mp_ipaddrcnt = 0; } } }