示例#1
0
/*ARGSUSED*/
void
ip_drop_input(char *str, mblk_t *mp, ill_t *ill)
{
	if (mp == NULL)
		return;

	if (IPH_HDR_VERSION(mp->b_rptr) == IPV4_VERSION) {
		ipha_t *ipha = (ipha_t *)mp->b_rptr;

		DTRACE_IP7(drop__in, mblk_t *, mp, conn_t *, NULL, void_ip_t *,
		    ipha, __dtrace_ipsr_ill_t *, ill, ipha_t *, ipha,
		    ip6_t *, NULL, int, 0);
	} else {
/*
 * Set the lengths in the packet and the transmit attributes.
 */
void
sctp_set_iplen(sctp_t *sctp, mblk_t *mp, ip_xmit_attr_t *ixa)
{
	uint16_t	sum = 0;
	ipha_t		*iph;
	ip6_t		*ip6h;
	mblk_t		*pmp = mp;
	boolean_t	isv4;

	isv4 = (IPH_HDR_VERSION(mp->b_rptr) == IPV4_VERSION);
	for (; pmp; pmp = pmp->b_cont)
		sum += pmp->b_wptr - pmp->b_rptr;

	ixa->ixa_pktlen = sum;
	if (isv4) {
		iph = (ipha_t *)mp->b_rptr;
		iph->ipha_length = htons(sum);
		ixa->ixa_ip_hdr_length = sctp->sctp_ip_hdr_len;
	} else {
		ip6h = (ip6_t *)mp->b_rptr;
		ip6h->ip6_plen = htons(sum - IPV6_HDR_LEN);
		ixa->ixa_ip_hdr_length = sctp->sctp_ip_hdr6_len;
	}
}
/*
 * Returns 0 if the check failed and the restart should be refused,
 * 1 if the check succeeded.
 */
int
sctp_secure_restart_check(mblk_t *pkt, sctp_chunk_hdr_t *ich, uint32_t ports,
    int sleep, sctp_stack_t *sctps, ip_recv_attr_t *ira)
{
	sctp_faddr_t *fp, *fphead = NULL;
	sctp_parm_hdr_t *ph;
	ssize_t remaining;
	int isv4;
	ipha_t *iph;
	ip6_t *ip6h;
	in6_addr_t hdraddr[1];
	int retval = 0;
	sctp_tf_t *tf;
	sctp_t *sctp;
	int compres;
	sctp_init_chunk_t *init;
	int nadded = 0;

	/* extract the address from the IP header */
	isv4 = (IPH_HDR_VERSION(pkt->b_rptr) == IPV4_VERSION);
	if (isv4) {
		iph = (ipha_t *)pkt->b_rptr;
		IN6_IPADDR_TO_V4MAPPED(iph->ipha_src, hdraddr);
	} else {
		ip6h = (ip6_t *)pkt->b_rptr;
		hdraddr[0] = ip6h->ip6_src;
	}

	/* Walk the params in the INIT [ACK], pulling out addr params */
	remaining = ntohs(ich->sch_len) - sizeof (*ich) -
	    sizeof (sctp_init_chunk_t);
	if (remaining < sizeof (*ph)) {
		/* no parameters; restart OK */
		return (1);
	}
	init = (sctp_init_chunk_t *)(ich + 1);
	ph = (sctp_parm_hdr_t *)(init + 1);

	while (ph != NULL) {
		sctp_faddr_t *fpa = NULL;

		/* params will have already been byteordered when validating */
		if (ph->sph_type == htons(PARM_ADDR4)) {
			if (remaining >= PARM_ADDR4_LEN) {
				in6_addr_t addr;
				IN6_INADDR_TO_V4MAPPED((struct in_addr *)
				    (ph + 1), &addr);
				fpa = kmem_cache_alloc(sctp_kmem_faddr_cache,
				    sleep);
				if (fpa == NULL) {
					goto done;
				}
				bzero(fpa, sizeof (*fpa));
				fpa->faddr = addr;
				fpa->next = NULL;
			}
		} else if (ph->sph_type == htons(PARM_ADDR6)) {
			if (remaining >= PARM_ADDR6_LEN) {
				fpa = kmem_cache_alloc(sctp_kmem_faddr_cache,
				    sleep);
				if (fpa == NULL) {
					goto done;
				}
				bzero(fpa, sizeof (*fpa));
				bcopy(ph + 1, &fpa->faddr,
				    sizeof (fpa->faddr));
				fpa->next = NULL;
			}
		}
		/* link in the new addr, if it was an addr param */
		if (fpa != NULL) {
			if (fphead == NULL) {
				fphead = fpa;
			} else {
				fpa->next = fphead;
				fphead = fpa;
			}
		}

		ph = sctp_next_parm(ph, &remaining);
	}

	if (fphead == NULL) {
		/* no addr parameters; restart OK */
		return (1);
	}

	/*
	 * got at least one; make sure the header's addr is
	 * in the list
	 */
	fp = sctp_lookup_faddr_nosctp(fphead, hdraddr);
	if (fp == NULL) {
		/* not included; add it now */
		fp = kmem_cache_alloc(sctp_kmem_faddr_cache, sleep);
		if (fp == NULL) {
			goto done;
		}
		bzero(fp, sizeof (*fp));
		fp->faddr = *hdraddr;
		fp->next = fphead;
		fphead = fp;
	}

	/*
	 * Now, we can finally do the check: For each sctp instance
	 * on the hash line for ports, compare its faddr set against
	 * the new one. If the new one is a strict subset of any
	 * existing sctp's faddrs, the restart is OK. However, if there
	 * is an overlap, this could be an attack, so return failure.
	 * If all sctp's faddrs are disjoint, this is a legitimate new
	 * association.
	 */
	tf = &(sctps->sctps_conn_fanout[SCTP_CONN_HASH(sctps, ports)]);
	mutex_enter(&tf->tf_lock);

	for (sctp = tf->tf_sctp; sctp; sctp = sctp->sctp_conn_hash_next) {
		if (ports != sctp->sctp_connp->conn_ports) {
			continue;
		}
		compres = sctp_compare_faddrsets(fphead, sctp->sctp_faddrs);
		if (compres <= SCTP_ADDR_SUBSET) {
			retval = 1;
			mutex_exit(&tf->tf_lock);
			goto done;
		}
		if (compres == SCTP_ADDR_OVERLAP) {
			dprint(1,
			    ("new assoc from %x:%x:%x:%x overlaps with %p\n",
			    SCTP_PRINTADDR(*hdraddr), (void *)sctp));
			/*
			 * While we still hold the lock, we need to
			 * figure out which addresses have been
			 * added so we can include them in the abort
			 * we will send back. Since these faddrs will
			 * never be used, we overload the rto field
			 * here, setting it to 0 if the address was
			 * not added, 1 if it was added.
			 */
			for (fp = fphead; fp; fp = fp->next) {
				if (sctp_lookup_faddr(sctp, &fp->faddr)) {
					fp->rto = 0;
				} else {
					fp->rto = 1;
					nadded++;
				}
			}
			mutex_exit(&tf->tf_lock);
			goto done;
		}
	}
	mutex_exit(&tf->tf_lock);

	/* All faddrs are disjoint; legit new association */
	retval = 1;

done:
	/* If are attempted adds, send back an abort listing the addrs */
	if (nadded > 0) {
		void *dtail;
		size_t dlen;

		dtail = kmem_alloc(PARM_ADDR6_LEN * nadded, KM_NOSLEEP);
		if (dtail == NULL) {
			goto cleanup;
		}

		ph = dtail;
		dlen = 0;
		for (fp = fphead; fp; fp = fp->next) {
			if (fp->rto == 0) {
				continue;
			}
			if (IN6_IS_ADDR_V4MAPPED(&fp->faddr)) {
				ipaddr_t addr4;

				ph->sph_type = htons(PARM_ADDR4);
				ph->sph_len = htons(PARM_ADDR4_LEN);
				IN6_V4MAPPED_TO_IPADDR(&fp->faddr, addr4);
				ph++;
				bcopy(&addr4, ph, sizeof (addr4));
				ph = (sctp_parm_hdr_t *)
				    ((char *)ph + sizeof (addr4));
				dlen += PARM_ADDR4_LEN;
			} else {
				ph->sph_type = htons(PARM_ADDR6);
				ph->sph_len = htons(PARM_ADDR6_LEN);
				ph++;
				bcopy(&fp->faddr, ph, sizeof (fp->faddr));
				ph = (sctp_parm_hdr_t *)
				    ((char *)ph + sizeof (fp->faddr));
				dlen += PARM_ADDR6_LEN;
			}
		}

		/* Send off the abort */
		sctp_send_abort(sctp, sctp_init2vtag(ich),
		    SCTP_ERR_RESTART_NEW_ADDRS, dtail, dlen, pkt, 0, B_TRUE,
		    ira);

		kmem_free(dtail, PARM_ADDR6_LEN * nadded);
	}

cleanup:
	/* Clean up */
	if (fphead) {
		sctp_faddr_t *fpn;
		for (fp = fphead; fp; fp = fpn) {
			fpn = fp->next;
			if (fp->ixa != NULL) {
				ixa_refrele(fp->ixa);
				fp->ixa = NULL;
			}
			kmem_cache_free(sctp_kmem_faddr_cache, fp);
		}
	}

	return (retval);
}
/*
 * Sets the address parameters given in the INIT chunk into sctp's
 * faddrs; if psctp is non-NULL, copies psctp's saddrs. If there are
 * no address parameters in the INIT chunk, a single faddr is created
 * from the ip hdr at the beginning of pkt.
 * If there already are existing addresses hanging from sctp, merge
 * them in, if the old info contains addresses which are not present
 * in this new info, get rid of them, and clean the pointers if there's
 * messages which have this as their target address.
 *
 * We also re-adjust the source address list here since the list may
 * contain more than what is actually part of the association. If
 * we get here from sctp_send_cookie_echo(), we are on the active
 * side and psctp will be NULL and ich will be the INIT-ACK chunk.
 * If we get here from sctp_accept_comm(), ich will be the INIT chunk
 * and psctp will the listening endpoint.
 *
 * INIT processing: When processing the INIT we inherit the src address
 * list from the listener. For a loopback or linklocal association, we
 * delete the list and just take the address from the IP header (since
 * that's how we created the INIT-ACK). Additionally, for loopback we
 * ignore the address params in the INIT. For determining which address
 * types were sent in the INIT-ACK we follow the same logic as in
 * creating the INIT-ACK. We delete addresses of the type that are not
 * supported by the peer.
 *
 * INIT-ACK processing: When processing the INIT-ACK since we had not
 * included addr params for loopback or linklocal addresses when creating
 * the INIT, we just use the address from the IP header. Further, for
 * loopback we ignore the addr param list. We mark addresses of the
 * type not supported by the peer as unconfirmed.
 *
 * In case of INIT processing we look for supported address types in the
 * supported address param, if present. In both cases the address type in
 * the IP header is supported as well as types for addresses in the param
 * list, if any.
 *
 * Once we have the supported address types sctp_check_saddr() runs through
 * the source address list and deletes or marks as unconfirmed address of
 * types not supported by the peer.
 *
 * Returns 0 on success, sys errno on failure
 */
int
sctp_get_addrparams(sctp_t *sctp, sctp_t *psctp, mblk_t *pkt,
    sctp_chunk_hdr_t *ich, uint_t *sctp_options)
{
	sctp_init_chunk_t	*init;
	ipha_t			*iph;
	ip6_t			*ip6h;
	in6_addr_t		hdrsaddr[1];
	in6_addr_t		hdrdaddr[1];
	sctp_parm_hdr_t		*ph;
	ssize_t			remaining;
	int			isv4;
	int			err;
	sctp_faddr_t		*fp;
	int			supp_af = 0;
	boolean_t		check_saddr = B_TRUE;
	in6_addr_t		curaddr;
	sctp_stack_t		*sctps = sctp->sctp_sctps;
	conn_t			*connp = sctp->sctp_connp;

	if (sctp_options != NULL)
		*sctp_options = 0;

	/* extract the address from the IP header */
	isv4 = (IPH_HDR_VERSION(pkt->b_rptr) == IPV4_VERSION);
	if (isv4) {
		iph = (ipha_t *)pkt->b_rptr;
		IN6_IPADDR_TO_V4MAPPED(iph->ipha_src, hdrsaddr);
		IN6_IPADDR_TO_V4MAPPED(iph->ipha_dst, hdrdaddr);
		supp_af |= PARM_SUPP_V4;
	} else {
		ip6h = (ip6_t *)pkt->b_rptr;
		hdrsaddr[0] = ip6h->ip6_src;
		hdrdaddr[0] = ip6h->ip6_dst;
		supp_af |= PARM_SUPP_V6;
	}

	/*
	 * Unfortunately, we can't delay this because adding an faddr
	 * looks for the presence of the source address (from the ire
	 * for the faddr) in the source address list. We could have
	 * delayed this if, say, this was a loopback/linklocal connection.
	 * Now, we just end up nuking this list and taking the addr from
	 * the IP header for loopback/linklocal.
	 */
	if (psctp != NULL && psctp->sctp_nsaddrs > 0) {
		ASSERT(sctp->sctp_nsaddrs == 0);

		err = sctp_dup_saddrs(psctp, sctp, KM_NOSLEEP);
		if (err != 0)
			return (err);
	}
	/*
	 * We will add the faddr before parsing the address list as this
	 * might be a loopback connection and we would not have to
	 * go through the list.
	 *
	 * Make sure the header's addr is in the list
	 */
	fp = sctp_lookup_faddr(sctp, hdrsaddr);
	if (fp == NULL) {
		/* not included; add it now */
		err = sctp_add_faddr(sctp, hdrsaddr, KM_NOSLEEP, B_TRUE);
		if (err != 0)
			return (err);

		/* sctp_faddrs will be the hdr addr */
		fp = sctp->sctp_faddrs;
	}
	/* make the header addr the primary */

	if (cl_sctp_assoc_change != NULL && psctp == NULL)
		curaddr = sctp->sctp_current->faddr;

	sctp->sctp_primary = fp;
	sctp->sctp_current = fp;
	sctp->sctp_mss = fp->sfa_pmss;

	/* For loopback connections & linklocal get address from the header */
	if (sctp->sctp_loopback || sctp->sctp_linklocal) {
		if (sctp->sctp_nsaddrs != 0)
			sctp_free_saddrs(sctp);
		if ((err = sctp_saddr_add_addr(sctp, hdrdaddr, 0)) != 0)
			return (err);
		/* For loopback ignore address list */
		if (sctp->sctp_loopback)
			return (0);
		check_saddr = B_FALSE;
	}

	/* Walk the params in the INIT [ACK], pulling out addr params */
	remaining = ntohs(ich->sch_len) - sizeof (*ich) -
	    sizeof (sctp_init_chunk_t);
	if (remaining < sizeof (*ph)) {
		if (check_saddr) {
			sctp_check_saddr(sctp, supp_af, psctp == NULL ?
			    B_FALSE : B_TRUE, hdrdaddr);
		}
		ASSERT(sctp_saddr_lookup(sctp, hdrdaddr, 0) != NULL);
		return (0);
	}

	init = (sctp_init_chunk_t *)(ich + 1);
	ph = (sctp_parm_hdr_t *)(init + 1);

	/* params will have already been byteordered when validating */
	while (ph != NULL) {
		if (ph->sph_type == htons(PARM_SUPP_ADDRS)) {
			int		plen;
			uint16_t	*p;
			uint16_t	addrtype;

			ASSERT(psctp != NULL);
			plen = ntohs(ph->sph_len);
			p = (uint16_t *)(ph + 1);
			while (plen > 0) {
				addrtype = ntohs(*p);
				switch (addrtype) {
					case PARM_ADDR6:
						supp_af |= PARM_SUPP_V6;
						break;
					case PARM_ADDR4:
						supp_af |= PARM_SUPP_V4;
						break;
					default:
						break;
				}
				p++;
				plen -= sizeof (*p);
			}
		} else if (ph->sph_type == htons(PARM_ADDR4)) {
			if (remaining >= PARM_ADDR4_LEN) {
				in6_addr_t addr;
				ipaddr_t ta;

				supp_af |= PARM_SUPP_V4;
				/*
				 * Screen out broad/multicasts & loopback.
				 * If the endpoint only accepts v6 address,
				 * go to the next one.
				 *
				 * Subnet broadcast check is done in
				 * sctp_add_faddr().  If the address is
				 * a broadcast address, it won't be added.
				 */
				bcopy(ph + 1, &ta, sizeof (ta));
				if (ta == 0 ||
				    ta == INADDR_BROADCAST ||
				    ta == htonl(INADDR_LOOPBACK) ||
				    CLASSD(ta) || connp->conn_ipv6_v6only) {
					goto next;
				}
				IN6_INADDR_TO_V4MAPPED((struct in_addr *)
				    (ph + 1), &addr);

				/* Check for duplicate. */
				if (sctp_lookup_faddr(sctp, &addr) != NULL)
					goto next;

				/* OK, add it to the faddr set */
				err = sctp_add_faddr(sctp, &addr, KM_NOSLEEP,
				    B_FALSE);
				/* Something is wrong...  Try the next one. */
				if (err != 0)
					goto next;
			}
		} else if (ph->sph_type == htons(PARM_ADDR6) &&
		    connp->conn_family == AF_INET6) {
			/* An v4 socket should not take v6 addresses. */
			if (remaining >= PARM_ADDR6_LEN) {
				in6_addr_t *addr6;

				supp_af |= PARM_SUPP_V6;
				addr6 = (in6_addr_t *)(ph + 1);
				/*
				 * Screen out link locals, mcast, loopback
				 * and bogus v6 address.
				 */
				if (IN6_IS_ADDR_LINKLOCAL(addr6) ||
				    IN6_IS_ADDR_MULTICAST(addr6) ||
				    IN6_IS_ADDR_LOOPBACK(addr6) ||
				    IN6_IS_ADDR_V4MAPPED(addr6)) {
					goto next;
				}
				/* Check for duplicate. */
				if (sctp_lookup_faddr(sctp, addr6) != NULL)
					goto next;

				err = sctp_add_faddr(sctp,
				    (in6_addr_t *)(ph + 1), KM_NOSLEEP,
				    B_FALSE);
				/* Something is wrong...  Try the next one. */
				if (err != 0)
					goto next;
			}
		} else if (ph->sph_type == htons(PARM_FORWARD_TSN)) {
			if (sctp_options != NULL)
				*sctp_options |= SCTP_PRSCTP_OPTION;
		} /* else; skip */

next:
		ph = sctp_next_parm(ph, &remaining);
	}
	if (check_saddr) {
		sctp_check_saddr(sctp, supp_af, psctp == NULL ? B_FALSE :
		    B_TRUE, hdrdaddr);
	}
	ASSERT(sctp_saddr_lookup(sctp, hdrdaddr, 0) != NULL);
	/*
	 * We have the right address list now, update clustering's
	 * knowledge because when we sent the INIT we had just added
	 * the address the INIT was sent to.
	 */
	if (psctp == NULL && cl_sctp_assoc_change != NULL) {
		uchar_t	*alist;
		size_t	asize;
		uchar_t	*dlist;
		size_t	dsize;

		asize = sizeof (in6_addr_t) * sctp->sctp_nfaddrs;
		alist = kmem_alloc(asize, KM_NOSLEEP);
		if (alist == NULL) {
			SCTP_KSTAT(sctps, sctp_cl_assoc_change);
			return (ENOMEM);
		}
		/*
		 * Just include the address the INIT was sent to in the
		 * delete list and send the entire faddr list. We could
		 * do it differently (i.e include all the addresses in the
		 * add list even if it contains the original address OR
		 * remove the original address from the add list etc.), but
		 * this seems reasonable enough.
		 */
		dsize = sizeof (in6_addr_t);
		dlist = kmem_alloc(dsize, KM_NOSLEEP);
		if (dlist == NULL) {
			kmem_free(alist, asize);
			SCTP_KSTAT(sctps, sctp_cl_assoc_change);
			return (ENOMEM);
		}
		bcopy(&curaddr, dlist, sizeof (curaddr));
		sctp_get_faddr_list(sctp, alist, asize);
		(*cl_sctp_assoc_change)(connp->conn_family, alist, asize,
		    sctp->sctp_nfaddrs, dlist, dsize, 1, SCTP_CL_PADDR,
		    (cl_sctp_handle_t)sctp);
		/* alist and dlist will be freed by the clustering module */
	}
	return (0);
}
示例#5
0
/* ARGSUSED */
void
ip_fanout_sctp(mblk_t *mp, ill_t *recv_ill, ipha_t *ipha,
    uint32_t ports, uint_t flags, boolean_t mctl_present, boolean_t ip_policy,
    uint_t ipif_seqid, zoneid_t zoneid)
{
	sctp_t *sctp;
	boolean_t isv4;
	conn_t *connp;
	mblk_t *first_mp;
	ip6_t *ip6h;
	in6_addr_t map_src, map_dst;
	in6_addr_t *src, *dst;

	first_mp = mp;
	if (mctl_present) {
		mp = first_mp->b_cont;
		ASSERT(mp != NULL);
	}

	/* Assume IP provides aligned packets - otherwise toss */
	if (!OK_32PTR(mp->b_rptr)) {
		BUMP_MIB(&ip_mib, ipInDiscards);
		freemsg(first_mp);
		return;
	}

	if (IPH_HDR_VERSION(ipha) == IPV6_VERSION) {
		ip6h = (ip6_t *)ipha;
		src = &ip6h->ip6_src;
		dst = &ip6h->ip6_dst;
		isv4 = B_FALSE;
	} else {
		ip6h = NULL;
		IN6_IPADDR_TO_V4MAPPED(ipha->ipha_src, &map_src);
		IN6_IPADDR_TO_V4MAPPED(ipha->ipha_dst, &map_dst);
		src = &map_src;
		dst = &map_dst;
		isv4 = B_TRUE;
	}
	if ((connp = sctp_fanout(src, dst, ports, ipif_seqid, zoneid, mp)) ==
	    NULL) {
		ip_fanout_sctp_raw(first_mp, recv_ill, ipha, isv4,
		    ports, mctl_present, flags, ip_policy,
		    ipif_seqid, zoneid);
		return;
	}
	sctp = CONN2SCTP(connp);

	/* Found a client; up it goes */
	BUMP_MIB(&ip_mib, ipInDelivers);

	/*
	 * We check some fields in conn_t without holding a lock.
	 * This should be fine.
	 */
	if (CONN_INBOUND_POLICY_PRESENT(connp) || mctl_present) {
		first_mp = ipsec_check_inbound_policy(first_mp, connp,
		    ipha, NULL, mctl_present);
		if (first_mp == NULL) {
			SCTP_REFRELE(sctp);
			return;
		}
	}

	/* Initiate IPPF processing for fastpath */
	if (IPP_ENABLED(IPP_LOCAL_IN)) {
		ip_process(IPP_LOCAL_IN, &mp,
		    recv_ill->ill_phyint->phyint_ifindex);
		if (mp == NULL) {
			SCTP_REFRELE(sctp);
			if (mctl_present)
				freeb(first_mp);
			return;
		} else if (mctl_present) {
			/*
			 * ip_process might return a new mp.
			 */
			ASSERT(first_mp != mp);
			first_mp->b_cont = mp;
		} else {
			first_mp = mp;
		}
	}

	if (connp->conn_recvif || connp->conn_recvslla ||
	    connp->conn_ipv6_recvpktinfo) {
		int in_flags = 0;

		if (connp->conn_recvif || connp->conn_ipv6_recvpktinfo) {
			in_flags = IPF_RECVIF;
		}
		if (connp->conn_recvslla) {
			in_flags |= IPF_RECVSLLA;
		}
		if (isv4) {
			mp = ip_add_info(mp, recv_ill, in_flags);
		} else {
			mp = ip_add_info_v6(mp, recv_ill, &ip6h->ip6_dst);
		}
		if (mp == NULL) {
			SCTP_REFRELE(sctp);
			if (mctl_present)
				freeb(first_mp);
			return;
		} else if (mctl_present) {
			/*
			 * ip_add_info might return a new mp.
			 */
			ASSERT(first_mp != mp);
			first_mp->b_cont = mp;
		} else {
			first_mp = mp;
		}
	}

	mutex_enter(&sctp->sctp_lock);
	if (sctp->sctp_running) {
		if (mctl_present)
			mp->b_prev = first_mp;
		if (!sctp_add_recvq(sctp, mp, B_FALSE)) {
			BUMP_MIB(&ip_mib, ipInDiscards);
			freemsg(first_mp);
		}
		mutex_exit(&sctp->sctp_lock);
	} else {
		sctp->sctp_running = B_TRUE;
		mutex_exit(&sctp->sctp_lock);

		mutex_enter(&sctp->sctp_recvq_lock);
		if (sctp->sctp_recvq != NULL) {
			if (mctl_present)
				mp->b_prev = first_mp;
			if (!sctp_add_recvq(sctp, mp, B_TRUE)) {
				BUMP_MIB(&ip_mib, ipInDiscards);
				freemsg(first_mp);
			}
			mutex_exit(&sctp->sctp_recvq_lock);
			WAKE_SCTP(sctp);
		} else {
			mutex_exit(&sctp->sctp_recvq_lock);
			sctp_input_data(sctp, mp, (mctl_present ? first_mp :
			    NULL));
			WAKE_SCTP(sctp);
			sctp_process_sendq(sctp);
		}
	}
	SCTP_REFRELE(sctp);
}
示例#6
0
/*
 * OOTB version of the above.
 * If iserror == 0, sends an abort. If iserror != 0, sends an error.
 */
void
sctp_ootb_send_abort(uint32_t vtag, uint16_t serror, char *details,
    size_t len, const mblk_t *inmp, int iserror, boolean_t tbit,
    ip_recv_attr_t *ira, ip_stack_t *ipst)
{
	uint32_t	ip_hdr_len;
	size_t		ahlen;
	ipha_t		*ipha = NULL;
	ip6_t		*ip6h = NULL;
	sctp_hdr_t	*insctph;
	int		i;
	uint16_t	port;
	ssize_t		alen;
	int		isv4;
	mblk_t		*mp;
	netstack_t	*ns = ipst->ips_netstack;
	sctp_stack_t	*sctps = ns->netstack_sctp;
	ip_xmit_attr_t	ixas;

	bzero(&ixas, sizeof (ixas));

	isv4 = (IPH_HDR_VERSION(inmp->b_rptr) == IPV4_VERSION);
	ip_hdr_len = ira->ira_ip_hdr_length;
	ahlen = ip_hdr_len + sizeof (sctp_hdr_t);

	/*
	 * If this is a labeled system, then check to see if we're allowed to
	 * send a response to this particular sender.  If not, then just drop.
	 */
	if (is_system_labeled() && !tsol_can_reply_error(inmp, ira))
		return;

	mp = allocb(ahlen + sctps->sctps_wroff_xtra, BPRI_MED);
	if (mp == NULL) {
		return;
	}
	mp->b_rptr += sctps->sctps_wroff_xtra;
	mp->b_wptr = mp->b_rptr + ahlen;
	bcopy(inmp->b_rptr, mp->b_rptr, ahlen);

	/*
	 * We follow the logic in tcp_xmit_early_reset() in that we skip
	 * reversing source route (i.e. replace all IP options with EOL).
	 */
	if (isv4) {
		ipaddr_t	v4addr;

		ipha = (ipha_t *)mp->b_rptr;
		for (i = IP_SIMPLE_HDR_LENGTH; i < (int)ip_hdr_len; i++)
			mp->b_rptr[i] = IPOPT_EOL;
		/* Swap addresses */
		ipha->ipha_length = htons(ahlen);
		v4addr = ipha->ipha_src;
		ipha->ipha_src = ipha->ipha_dst;
		ipha->ipha_dst = v4addr;
		ipha->ipha_ident = 0;
		ipha->ipha_ttl = (uchar_t)sctps->sctps_ipv4_ttl;

		ixas.ixa_flags = IXAF_BASIC_SIMPLE_V4;
	} else {
		in6_addr_t	v6addr;

		ip6h = (ip6_t *)mp->b_rptr;
		/* Remove any extension headers assuming partial overlay */
		if (ip_hdr_len > IPV6_HDR_LEN) {
			uint8_t	*to;

			to = mp->b_rptr + ip_hdr_len - IPV6_HDR_LEN;
			ovbcopy(ip6h, to, IPV6_HDR_LEN);
			mp->b_rptr += ip_hdr_len - IPV6_HDR_LEN;
			ip_hdr_len = IPV6_HDR_LEN;
			ip6h = (ip6_t *)mp->b_rptr;
			ip6h->ip6_nxt = IPPROTO_SCTP;
			ahlen = ip_hdr_len + sizeof (sctp_hdr_t);
		}
		ip6h->ip6_plen = htons(ahlen - IPV6_HDR_LEN);
		v6addr = ip6h->ip6_src;
		ip6h->ip6_src = ip6h->ip6_dst;
		ip6h->ip6_dst = v6addr;
		ip6h->ip6_hops = (uchar_t)sctps->sctps_ipv6_hoplimit;

		ixas.ixa_flags = IXAF_BASIC_SIMPLE_V6;
		if (IN6_IS_ADDR_LINKSCOPE(&ip6h->ip6_dst)) {
			ixas.ixa_flags |= IXAF_SCOPEID_SET;
			ixas.ixa_scopeid = ira->ira_ruifindex;
		}
	}
	insctph = (sctp_hdr_t *)(mp->b_rptr + ip_hdr_len);

	/* Swap ports.  Verification tag is reused. */
	port = insctph->sh_sport;
	insctph->sh_sport = insctph->sh_dport;
	insctph->sh_dport = port;
	insctph->sh_verf = vtag;

	/* Link in the abort chunk */
	if ((alen = sctp_link_abort(mp, serror, details, len, iserror, tbit))
	    < 0) {
		freemsg(mp);
		return;
	}

	ixas.ixa_pktlen = ahlen + alen;
	ixas.ixa_ip_hdr_length = ip_hdr_len;

	if (isv4) {
		ipha->ipha_length = htons(ixas.ixa_pktlen);
	} else {
		ip6h->ip6_plen = htons(ixas.ixa_pktlen - IPV6_HDR_LEN);
	}

	ixas.ixa_protocol = IPPROTO_SCTP;
	ixas.ixa_zoneid = ira->ira_zoneid;
	ixas.ixa_ipst = ipst;
	ixas.ixa_ifindex = 0;

	SCTPS_BUMP_MIB(sctps, sctpAborted);

	if (is_system_labeled()) {
		ASSERT(ira->ira_tsl != NULL);

		ixas.ixa_tsl = ira->ira_tsl;	/* A multi-level responder */
	}

	if (ira->ira_flags & IRAF_IPSEC_SECURE) {
		/*
		 * Apply IPsec based on how IPsec was applied to
		 * the packet that was out of the blue.
		 */
		if (!ipsec_in_to_out(ira, &ixas, mp, ipha, ip6h)) {
			BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsOutDiscards);
			/* Note: mp already consumed and ip_drop_packet done */
			return;
		}
	} else {
		/*
		 * This is in clear. The abort message we are building
		 * here should go out in clear, independent of our policy.
		 */
		ixas.ixa_flags |= IXAF_NO_IPSEC;
	}

	(void) ip_output_simple(mp, &ixas);
	ixa_cleanup(&ixas);
}
示例#7
0
/*
 * If iserror == 0, sends an abort. If iserror != 0, sends an error.
 */
void
sctp_send_abort(sctp_t *sctp, uint32_t vtag, uint16_t serror, char *details,
    size_t len, mblk_t *inmp, int iserror, boolean_t tbit, ip_recv_attr_t *ira)
{

	mblk_t		*hmp;
	uint32_t	ip_hdr_len;
	ipha_t		*iniph;
	ipha_t		*ahiph = NULL;
	ip6_t		*inip6h;
	ip6_t		*ahip6h = NULL;
	sctp_hdr_t	*sh;
	sctp_hdr_t	*insh;
	size_t		ahlen;
	uchar_t		*p;
	ssize_t		alen;
	int		isv4;
	conn_t		*connp = sctp->sctp_connp;
	sctp_stack_t	*sctps = sctp->sctp_sctps;
	ip_xmit_attr_t	*ixa;

	isv4 = (IPH_HDR_VERSION(inmp->b_rptr) == IPV4_VERSION);
	if (isv4) {
		ahlen = sctp->sctp_hdr_len;
	} else {
		ahlen = sctp->sctp_hdr6_len;
	}

	/*
	 * If this is a labeled system, then check to see if we're allowed to
	 * send a response to this particular sender.  If not, then just drop.
	 */
	if (is_system_labeled() && !tsol_can_reply_error(inmp, ira))
		return;

	hmp = allocb(sctps->sctps_wroff_xtra + ahlen, BPRI_MED);
	if (hmp == NULL) {
		/* XXX no resources */
		return;
	}

	/* copy in the IP / SCTP header */
	p = hmp->b_rptr + sctps->sctps_wroff_xtra;
	hmp->b_rptr = p;
	hmp->b_wptr = p + ahlen;
	if (isv4) {
		bcopy(sctp->sctp_iphc, p, sctp->sctp_hdr_len);
		/*
		 * Composite is likely incomplete at this point, so pull
		 * info from the incoming IP / SCTP headers.
		 */
		ahiph = (ipha_t *)p;
		iniph = (ipha_t *)inmp->b_rptr;
		ip_hdr_len = IPH_HDR_LENGTH(inmp->b_rptr);

		sh = (sctp_hdr_t *)(p + sctp->sctp_ip_hdr_len);
		ASSERT(OK_32PTR(sh));

		insh = (sctp_hdr_t *)((uchar_t *)iniph + ip_hdr_len);
		ASSERT(OK_32PTR(insh));

		/* Copy in the peer's IP addr */
		ahiph->ipha_dst = iniph->ipha_src;
		ahiph->ipha_src = iniph->ipha_dst;
	} else {
		bcopy(sctp->sctp_iphc6, p, sctp->sctp_hdr6_len);
		ahip6h = (ip6_t *)p;
		inip6h = (ip6_t *)inmp->b_rptr;
		ip_hdr_len = ip_hdr_length_v6(inmp, inip6h);

		sh = (sctp_hdr_t *)(p + sctp->sctp_ip_hdr6_len);
		ASSERT(OK_32PTR(sh));

		insh = (sctp_hdr_t *)((uchar_t *)inip6h + ip_hdr_len);
		ASSERT(OK_32PTR(insh));

		/* Copy in the peer's IP addr */
		ahip6h->ip6_dst = inip6h->ip6_src;
		ahip6h->ip6_src = inip6h->ip6_dst;
	}

	/* Fill in the holes in the SCTP common header */
	sh->sh_sport = insh->sh_dport;
	sh->sh_dport = insh->sh_sport;
	sh->sh_verf = vtag;

	/* Link in the abort chunk */
	if ((alen = sctp_link_abort(hmp, serror, details, len, iserror, tbit))
	    < 0) {
		freemsg(hmp);
		return;
	}

	/*
	 * Base the transmission on any routing-related socket options
	 * that have been set on the listener/connection.
	 */
	ixa = conn_get_ixa_exclusive(connp);
	if (ixa == NULL) {
		freemsg(hmp);
		return;
	}
	ixa->ixa_flags &= ~IXAF_VERIFY_PMTU;

	ixa->ixa_pktlen = ahlen + alen;
	if (isv4) {
		ixa->ixa_flags |= IXAF_IS_IPV4;
		ahiph->ipha_length = htons(ixa->ixa_pktlen);
		ixa->ixa_ip_hdr_length = sctp->sctp_ip_hdr_len;
	} else {
		ixa->ixa_flags &= ~IXAF_IS_IPV4;
		ahip6h->ip6_plen = htons(ixa->ixa_pktlen - IPV6_HDR_LEN);
		ixa->ixa_ip_hdr_length = sctp->sctp_ip_hdr6_len;
	}

	SCTPS_BUMP_MIB(sctps, sctpAborted);
	BUMP_LOCAL(sctp->sctp_obchunks);

	if (is_system_labeled() && ixa->ixa_tsl != NULL) {
		ASSERT(ira->ira_tsl != NULL);

		ixa->ixa_tsl = ira->ira_tsl;	/* A multi-level responder */
	}

	if (ira->ira_flags & IRAF_IPSEC_SECURE) {
		/*
		 * Apply IPsec based on how IPsec was applied to
		 * the packet that caused the abort.
		 */
		if (!ipsec_in_to_out(ira, ixa, hmp, ahiph, ahip6h)) {
			ip_stack_t *ipst = sctps->sctps_netstack->netstack_ip;

			BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsOutDiscards);
			/* Note: mp already consumed and ip_drop_packet done */
			ixa_refrele(ixa);
			return;
		}
	} else {
		ixa->ixa_flags |= IXAF_NO_IPSEC;
	}

	BUMP_LOCAL(sctp->sctp_opkts);
	BUMP_LOCAL(sctp->sctp_obchunks);

	(void) ip_output_simple(hmp, ixa);
	ixa_refrele(ixa);
}
/* ARGSUSED */
static int
ipgpc_invoke_action(ipp_action_id_t aid, ipp_packet_t *packet)
{
	ipgpc_class_t *out_class;
	hrtime_t start, end;
	mblk_t *mp = NULL;
	ip_priv_t *priv = NULL;
	ill_t *ill = NULL;
	ipha_t *ipha;
	ip_proc_t callout_pos;
	int af;
	int rc;
	ipgpc_packet_t pkt;
	uint_t ill_idx;

	/* extract packet data */
	mp = ipp_packet_get_data(packet);
	ASSERT(mp != NULL);

	priv = (ip_priv_t *)ipp_packet_get_private(packet);
	ASSERT(priv != NULL);

	callout_pos = priv->proc;
	ill_idx = priv->ill_index;

	/* If we don't get an M_DATA, then return an error */
	if (mp->b_datap->db_type != M_DATA) {
		if ((mp->b_cont != NULL) &&
		    (mp->b_cont->b_datap->db_type == M_DATA)) {
			mp = mp->b_cont; /* jump over the M_CTL into M_DATA */
		} else {
			ipgpc0dbg(("ipgpc_invoke_action: no data\n"));
			atomic_add_64(&ipgpc_epackets, 1);
			return (EINVAL);
		}
	}

	/*
	 * Translate the callout_pos into the direction the packet is traveling
	 */
	if (callout_pos != IPP_LOCAL_IN) {
		if (callout_pos & IPP_LOCAL_OUT) {
			callout_pos = IPP_LOCAL_OUT;
		} else if (callout_pos & IPP_FWD_IN) {
			callout_pos = IPP_FWD_IN;
		} else {	/* IPP_FWD_OUT */
			callout_pos = IPP_FWD_OUT;
		}
	}

	/* parse the packet from the message block */
	ipha = (ipha_t *)mp->b_rptr;
	/* Determine IP Header Version */
	if (IPH_HDR_VERSION(ipha) == IPV4_VERSION) {
		parse_packet(&pkt, mp);
		af = AF_INET;
	} else {
		parse_packet6(&pkt, mp);
		af = AF_INET6;
	}

	pkt.direction = callout_pos; /* set packet direction */

	/* The ill_index could be 0 when called from forwarding (read) path */
	if (ill_idx > 0)
		ill = ill_lookup_on_ifindex_global_instance(ill_idx, B_FALSE);

	if (ill != NULL) {
		/*
		 * Since all IPP actions in an IPMP group are performed
		 * relative to the IPMP group interface, if this is an
		 * underlying interface in an IPMP group, use the IPMP
		 * group interface's index.
		 */
		if (IS_UNDER_IPMP(ill))
			pkt.if_index = ipmp_ill_get_ipmp_ifindex(ill);
		else
			pkt.if_index = ill->ill_phyint->phyint_ifindex;
		/* Got the field from the ILL, go ahead and refrele */
		ill_refrele(ill);
	} else {
		/* unknown if_index */
		pkt.if_index = IPGPC_UNSPECIFIED;
	}

	if (ipgpc_debug > 5) {
		/* print pkt under high debug level */
#ifdef	IPGPC_DEBUG
		print_packet(af, &pkt);
#endif
	}
	if (ipgpc_debug > 3) {
		start = gethrtime(); /* start timer */
	}

	/* classify this packet */
	out_class = ipgpc_classify(af, &pkt);

	if (ipgpc_debug > 3) {
		end = gethrtime(); /* stop timer */
	}

	/* ipgpc_classify will only return NULL if a memory error occured */
	if (out_class == NULL) {
		atomic_add_64(&ipgpc_epackets, 1);
		return (ENOMEM);
	}

	ipgpc1dbg(("ipgpc_invoke_action: class = %s", out_class->class_name));
	/* print time to classify(..) */
	ipgpc2dbg(("ipgpc_invoke_action: time = %lld nsec\n", (end - start)));

	if ((rc = ipp_packet_add_class(packet, out_class->class_name,
	    out_class->next_action)) != 0) {
		atomic_add_64(&ipgpc_epackets, 1);
		ipgpc0dbg(("ipgpc_invoke_action: ipp_packet_add_class " \
		    "failed with error %d", rc));
		return (rc);
	}
	return (ipp_packet_next(packet, IPP_ACTION_CONT));
}
示例#9
0
/* ARGSUSED */
int
tswtcl_process(mblk_t **mpp, tswtcl_data_t *tswtcl_data,
    ipp_action_id_t *next_action)
{
	ipha_t *ipha;
	hrtime_t now;
	ip6_t *ip6_hdr;
	uint32_t pkt_len;
	mblk_t *mp = *mpp;
	hrtime_t deltaT;
	uint64_t bitsinwin;
	uint32_t min = 0, additive, rnd;
	tswtcl_cfg_t *cfg_parms = tswtcl_data->cfg_parms;

	if (mp == NULL) {
		tswtcl0dbg(("tswtcl_process: null mp!\n"));
		atomic_add_64(&tswtcl_data->epackets, 1);
		return (EINVAL);
	}

	if (mp->b_datap->db_type != M_DATA) {
		if ((mp->b_cont != NULL) &&
		    (mp->b_cont->b_datap->db_type == M_DATA)) {
			mp = mp->b_cont;
		} else {
			tswtcl0dbg(("tswtcl_process: no data\n"));
			atomic_add_64(&tswtcl_data->epackets, 1);
			return (EINVAL);
		}
	}

	/* Figure out the ToS/Traffic Class and length from the message */
	if ((mp->b_wptr - mp->b_rptr) < IP_SIMPLE_HDR_LENGTH) {
		if (!pullupmsg(mp, IP_SIMPLE_HDR_LENGTH)) {
			tswtcl0dbg(("tswtcl_process: pullup error\n"));
			atomic_add_64(&tswtcl_data->epackets, 1);
			return (EINVAL);
		}
	}
	ipha = (ipha_t *)mp->b_rptr;
	if (IPH_HDR_VERSION(ipha) == IPV4_VERSION) {
		pkt_len = ntohs(ipha->ipha_length);
	} else {
		ip6_hdr = (ip6_t *)mp->b_rptr;
		pkt_len = ntohs(ip6_hdr->ip6_plen) +
		    ip_hdr_length_v6(mp, ip6_hdr);
	}

	/* Convert into bits */
	pkt_len <<= 3;

	/* Get current time */
	now = gethrtime();

	/* Update the avg_rate and win_front tswtcl_data */
	mutex_enter(&tswtcl_data->tswtcl_lock);

	/* avg_rate = bits/sec and window in msec */
	bitsinwin = ((uint64_t)tswtcl_data->avg_rate * cfg_parms->window /
	    1000) + pkt_len;

	deltaT = now - tswtcl_data->win_front + cfg_parms->nsecwindow;

	tswtcl_data->avg_rate = (uint64_t)bitsinwin * METER_SEC_TO_NSEC /
	    deltaT;
	tswtcl_data->win_front = now;

	if (tswtcl_data->avg_rate <= cfg_parms->committed_rate) {
		*next_action = cfg_parms->green_action;
	} else if (tswtcl_data->avg_rate <= cfg_parms->peak_rate) {
		/*
		 * Compute the probability:
		 *
		 * p0 = (avg_rate - committed_rate) / avg_rate
		 *
		 * Yellow with probability p0
		 * Green with probability (1 - p0)
		 *
		 */
		uint32_t aminusc;

		/* Get a random no. betweeen 0 and avg_rate */
		(void) random_get_pseudo_bytes((uint8_t *)&additive,
		    sizeof (additive));
		rnd = min + (additive % (tswtcl_data->avg_rate - min + 1));

		aminusc = tswtcl_data->avg_rate - cfg_parms->committed_rate;
		if (aminusc >= rnd) {
			*next_action = cfg_parms->yellow_action;
		} else {
			*next_action = cfg_parms->green_action;
		}
	} else {
		/*
		 * Compute the probability:
		 *
		 * p1 = (avg_rate - peak_rate) / avg_rate
		 * p2 = (peak_rate - committed_rate) / avg_rate
		 *
		 * Red with probability p1
		 * Yellow with probability p2
		 * Green with probability (1 - (p1 + p2))
		 *
		 */
		uint32_t  aminusp;

		/* Get a random no. betweeen 0 and avg_rate */
		(void) random_get_pseudo_bytes((uint8_t *)&additive,
		    sizeof (additive));
		rnd = min + (additive % (tswtcl_data->avg_rate - min + 1));

		aminusp = tswtcl_data->avg_rate - cfg_parms->peak_rate;

		if (aminusp >= rnd) {
			*next_action = cfg_parms->red_action;
		} else if ((cfg_parms->pminusc + aminusp) >= rnd) {
			*next_action = cfg_parms->yellow_action;
		} else {
			*next_action = cfg_parms->green_action;
		}

	}
	mutex_exit(&tswtcl_data->tswtcl_lock);

	/* Update Stats */
	if (*next_action == cfg_parms->green_action) {
		atomic_add_64(&tswtcl_data->green_packets, 1);
		atomic_add_64(&tswtcl_data->green_bits, pkt_len);
	} else if (*next_action == cfg_parms->yellow_action) {
		atomic_add_64(&tswtcl_data->yellow_packets, 1);
		atomic_add_64(&tswtcl_data->yellow_bits, pkt_len);
	} else {
		ASSERT(*next_action == cfg_parms->red_action);
		atomic_add_64(&tswtcl_data->red_packets, 1);
		atomic_add_64(&tswtcl_data->red_bits, pkt_len);
	}
	return (0);
}