/*
 * 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);
}
Esempio n. 2
0
/*
 * Returns 0 on non-fatal error, otherwise a system error on fatal
 * error.
 */
int
sctp_handle_error(sctp_t *sctp, sctp_hdr_t *sctph, sctp_chunk_hdr_t *ch,
    mblk_t *mp, ip_recv_attr_t *ira)
{
	sctp_parm_hdr_t *errh;
	sctp_chunk_hdr_t *uch;

	if (ch->sch_len == htons(sizeof (*ch))) {
		/* no error cause given */
		return (0);
	}
	errh = (sctp_parm_hdr_t *)(ch + 1);
	sctp_error_event(sctp, ch, B_FALSE);

	switch (errh->sph_type) {
	/*
	 * Both BAD_SID and NO_USR_DATA errors
	 * indicate a serious bug in our stack,
	 * so complain and abort the association.
	 */
	case SCTP_ERR_BAD_SID:
		cmn_err(CE_WARN, "BUG! send to invalid SID");
		sctp_send_abort(sctp, sctph->sh_verf, 0, NULL, 0, mp, 0, 0,
		    ira);
		return (ECONNABORTED);
	case SCTP_ERR_NO_USR_DATA:
		cmn_err(CE_WARN, "BUG! no usr data");
		sctp_send_abort(sctp, sctph->sh_verf, 0, NULL, 0, mp, 0, 0,
		    ira);
		return (ECONNABORTED);
	case SCTP_ERR_UNREC_CHUNK:
		/* Pull out the unrecognized chunk type */
		if (ntohs(errh->sph_len) < (sizeof (*errh) + sizeof (*uch))) {
			/* Not enough to process */
			return (0);
		}
		uch = (sctp_chunk_hdr_t *)(errh + 1);
		if (uch->sch_id == CHUNK_ASCONF) {
			/* Turn on ASCONF sending */
			sctp->sctp_understands_asconf = B_FALSE;
			/*
			 * Hand off to asconf to clear out the unacked
			 * asconf chunk.
			 */
			if (ntohs(uch->sch_len) !=
			    (ntohs(errh->sph_len) - sizeof (*errh))) {
				/* malformed */
				dprint(0, ("Malformed Unrec Chunk error\n"));
				return (0);
			}
			sctp_asconf_free_cxmit(sctp, uch);
			return (0);
		}
		/* Else drop it */
		break;
	default:
		break;
	}

	return (0);
}