/*
 * 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);
}
int
sctp_set_peerprim(sctp_t *sctp, const void *inp)
{
	const struct sctp_setprim	*prim = inp;
	const struct sockaddr_storage	*ss;
	struct sockaddr_in *sin;
	struct sockaddr_in6 *sin6;
	in6_addr_t addr;
	mblk_t *mp;
	sctp_saddr_ipif_t *sp;
	sctp_addip4_t *ad4;
	sctp_addip6_t *ad6;
	sctp_asconf_t asc[1];
	int error = 0;
	uint_t	ifindex = 0;

	/* Does the peer understand ASCONF and Add-IP? */
	if (!sctp->sctp_understands_asconf || !sctp->sctp_understands_addip) {
		return (EOPNOTSUPP);
	}

	/* Don't do anything if we are not connected */
	if (sctp->sctp_state != SCTPS_ESTABLISHED)
		return (EINVAL);

	ss = &prim->ssp_addr;
	sin = NULL;
	sin6 = NULL;
	if (ss->ss_family == AF_INET) {
		sin = (struct sockaddr_in *)ss;
		IN6_IPADDR_TO_V4MAPPED(sin->sin_addr.s_addr, &addr);
	} else if (ss->ss_family == AF_INET6) {
		sin6 = (struct sockaddr_in6 *)ss;
		addr = sin6->sin6_addr;
		ifindex = sin6->sin6_scope_id;
	} else {
		return (EAFNOSUPPORT);
	}
	sp = sctp_saddr_lookup(sctp, &addr, ifindex);
	if (sp == NULL)
		return (EADDRNOTAVAIL);
	sctp_asconf_init(asc);
	if (sin) {
		mp = allocb(sizeof (*ad4), BPRI_MED);
		if (mp == NULL) {
			error = ENOMEM;
			goto fail;
		}
		mp->b_wptr += sizeof (*ad4);
		ad4 = (sctp_addip4_t *)mp->b_rptr;
		ad4->sad4_addip_ph.sph_type = htons(PARM_SET_PRIMARY);
		ad4->sad4_addip_ph.sph_len = htons(sizeof (sctp_parm_hdr_t) +
		    PARM_ADDR4_LEN + sizeof (ad4->asconf_req_cid));
		ad4->sad4_addr4_ph.sph_type = htons(PARM_ADDR4);
		ad4->sad4_addr4_ph.sph_len = htons(PARM_ADDR4_LEN);
		ad4->sad4_addr = sin->sin_addr.s_addr;
	} else {
		mp = allocb(sizeof (*ad6), BPRI_MED);
		if (mp == NULL) {
			error = ENOMEM;
			goto fail;
		}
		mp->b_wptr += sizeof (*ad6);
		ad6 = (sctp_addip6_t *)mp->b_rptr;
		ad6->sad6_addip_ph.sph_type = htons(PARM_SET_PRIMARY);
		ad6->sad6_addip_ph.sph_len = htons(sizeof (sctp_parm_hdr_t) +
		    PARM_ADDR6_LEN + sizeof (ad6->asconf_req_cid));
		ad6->sad6_addr6_ph.sph_type = htons(PARM_ADDR6);
		ad6->sad6_addr6_ph.sph_len = htons(PARM_ADDR6_LEN);
		ad6->sad6_addr = sin6->sin6_addr;
	}

	error = sctp_asconf_add(asc, mp);
	if (error != 0) {
		goto fail;
	}

	error = sctp_asconf_send(sctp, asc, sctp->sctp_current, NULL);
	if (error == 0) {
		return (0);
	}

fail:
	sctp_asconf_destroy(asc);
	return (error);
}
/*
 * 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);
}
/*ARGSUSED*/
static void
sctp_addip_ack(sctp_t *sctp, sctp_parm_hdr_t *ph, sctp_parm_hdr_t *oph,
    sctp_faddr_t *fp, in6_addr_t *laddr)
{
	in6_addr_t		addr;
	sctp_saddr_ipif_t	*sp;
	ipaddr_t		*addr4;
	boolean_t		backout = B_FALSE;
	uint16_t		type;
	uint32_t		*cid;

	/* could be an ASSERT */
	if (laddr != NULL)
		IN6_IPADDR_TO_V4MAPPED(0, laddr);

	/* If the peer doesn't understand Add-IP, remember it */
	if (ph != NULL && ph->sph_type == htons(PARM_UNRECOGNIZED)) {
		sctp->sctp_understands_addip = B_FALSE;
		backout = B_TRUE;
	}

	/*
	 * If OK, continue with the add / delete action, otherwise
	 * back out the action.
	 */
	if (ph != NULL && ph->sph_type != htons(PARM_SUCCESS)) {
		backout = B_TRUE;
		sctp_error_event(sctp, (sctp_chunk_hdr_t *)ph);
	}

	type = ntohs(oph->sph_type);
	cid = (uint32_t *)(oph + 1);
	oph = (sctp_parm_hdr_t *)(cid + 1);
	if (oph->sph_type == htons(PARM_ADDR4)) {
		addr4 = (ipaddr_t *)(oph + 1);
		IN6_IPADDR_TO_V4MAPPED(*addr4, &addr);
	} else {
		bcopy(oph + 1, &addr, sizeof (addr));
	}

	/* Signifies that the address was sucessfully processed */
	if (!backout && laddr != NULL)
		*laddr = addr;

	sp = sctp_saddr_lookup(sctp, &addr, 0);
	ASSERT(sp != NULL);

	if (type == PARM_ADD_IP) {
		if (backout) {
			sctp_del_saddr(sctp, sp);
		} else {
			sp->saddr_ipif_dontsrc = 0;
		}
	} else if (type == PARM_DEL_IP) {
		if (backout) {
			sp->saddr_ipif_delete_pending = 0;
			sp->saddr_ipif_dontsrc = 0;
		} else {
			sctp_del_saddr(sctp, sp);
		}
	} else {
		/* Must be either PARM_ADD_IP or PARM_DEL_IP */
		ASSERT(0);
	}
}
int
sctp_del_ip(sctp_t *sctp, const void *addrs, uint32_t cnt, uchar_t *ulist,
    size_t usize)
{
	struct sockaddr_in	*sin4;
	struct sockaddr_in6	*sin6;
	mblk_t			*mp;
	int			error = 0;
	int			i;
	int			addrcnt = 0;
	sctp_addip4_t		*ad4;
	sctp_addip6_t		*ad6;
	sctp_asconf_t		asc[1];
	sctp_saddr_ipif_t	*nsp;
	uint16_t		type = htons(PARM_DEL_IP);
	boolean_t		v4mapped = B_FALSE;
	in6_addr_t		addr;
	boolean_t		asconf = B_TRUE;
	uint_t			ifindex;
	sctp_cl_ainfo_t		*ainfo = NULL;
	uchar_t			*p = ulist;
	boolean_t		check_lport = B_FALSE;
	sctp_stack_t		*sctps = sctp->sctp_sctps;
	conn_t			*connp = sctp->sctp_connp;

	/* Does the peer understand ASCONF and Add-IP? */
	if (sctp->sctp_state <= SCTPS_LISTEN || !sctps->sctps_addip_enabled ||
	    !sctp->sctp_understands_asconf || !sctp->sctp_understands_addip) {
		asconf = B_FALSE;
	}

	if (sctp->sctp_state > SCTPS_BOUND)
		check_lport = B_TRUE;

	if (asconf) {
		/*
		 * On a clustered node, we need to pass this list when
		 * we get an ASCONF-ACK. We only pre-allocate memory for the
		 * list, but fill in the addresses when it is processed
		 * successfully after we get an ASCONF-ACK.
		 */
		if (cl_sctp_assoc_change != NULL) {
			ainfo = kmem_alloc(sizeof (*ainfo), KM_SLEEP);
			ainfo->sctp_cl_dsize = sizeof (in6_addr_t) * cnt;
			ainfo->sctp_cl_dlist = kmem_alloc(ainfo->sctp_cl_dsize,
			    KM_SLEEP);
		}
		sctp_asconf_init(asc);
	}
	/*
	 * Screen addresses:
	 * If adding:
	 *   o Must not already be a part of the association
	 *   o Must be AF_INET or AF_INET6
	 *   o XXX Must be valid source address for this node
	 *   o Must be unicast
	 *   o XXX Must fit scoping rules
	 * If deleting:
	 *   o Must be part of the association
	 */
	for (i = 0; i < cnt; i++) {
		ifindex = 0;

		switch (connp->conn_family) {
		case AF_INET:
			sin4 = (struct sockaddr_in *)addrs + i;
			if (check_lport &&
			    sin4->sin_port != connp->conn_lport) {
				error = EINVAL;
				goto fail;
			}
			v4mapped = B_TRUE;
			IN6_IPADDR_TO_V4MAPPED(sin4->sin_addr.s_addr, &addr);
			break;

		case AF_INET6:
			sin6 = (struct sockaddr_in6 *)addrs + i;
			if (check_lport &&
			    sin6->sin6_port != connp->conn_lport) {
				error = EINVAL;
				goto fail;
			}
			addr = sin6->sin6_addr;
			ifindex = sin6->sin6_scope_id;
			break;
		}
		nsp = sctp_saddr_lookup(sctp, &addr, ifindex);
		if (nsp == NULL) {
			error = EADDRNOTAVAIL;
			goto fail;
		}

		/* Collect the list of addresses, if required */
		if (usize >= sizeof (addr)) {
			bcopy(&addr, p, sizeof (addr));
			p += sizeof (addr);
			usize -= sizeof (addr);
		}
		if (!asconf)
			continue;

		nsp->saddr_ipif_delete_pending = 1;
		nsp->saddr_ipif_dontsrc = 1;
		addrcnt++;
		if (v4mapped) {
			mp = allocb(sizeof (*ad4), BPRI_MED);
			if (mp == NULL) {
				error = ENOMEM;
				goto fail;
			}
			mp->b_wptr += sizeof (*ad4);
			ad4 = (sctp_addip4_t *)mp->b_rptr;
			ad4->sad4_addip_ph.sph_type = type;
			ad4->sad4_addip_ph.sph_len =
			    htons(sizeof (sctp_parm_hdr_t) +
			    PARM_ADDR4_LEN + sizeof (ad4->asconf_req_cid));
			ad4->sad4_addr4_ph.sph_type = htons(PARM_ADDR4);
			ad4->sad4_addr4_ph.sph_len = htons(PARM_ADDR4_LEN);
			ad4->sad4_addr = sin4->sin_addr.s_addr;
		} else {
			mp = allocb(sizeof (*ad6), BPRI_MED);
			if (mp == NULL) {
				error = ENOMEM;
				goto fail;
			}
			mp->b_wptr += sizeof (*ad6);
			ad6 = (sctp_addip6_t *)mp->b_rptr;
			ad6->sad6_addip_ph.sph_type = type;
			ad6->sad6_addip_ph.sph_len =
			    htons(sizeof (sctp_parm_hdr_t) + PARM_ADDR6_LEN +
			    sizeof (ad6->asconf_req_cid));
			ad6->sad6_addr6_ph.sph_type = htons(PARM_ADDR6);
			ad6->sad6_addr6_ph.sph_len = htons(PARM_ADDR6_LEN);
			ad6->sad6_addr = addr;
		}

		error = sctp_asconf_add(asc, mp);
		if (error != 0)
			goto fail;
	}

	if (!asconf) {
		sctp_del_saddr_list(sctp, addrs, cnt, B_FALSE);
		return (0);
	}
	error = sctp_asconf_send(sctp, asc, sctp->sctp_current, ainfo);
	if (error != 0)
		goto fail;
	sctp_redo_faddr_srcs(sctp);
	return (0);

fail:
	if (ainfo != NULL) {
		kmem_free(ainfo->sctp_cl_dlist, ainfo->sctp_cl_dsize);
		ainfo->sctp_cl_dsize = 0;
		kmem_free(ainfo, sizeof (*ainfo));
	}
	if (!asconf)
		return (error);
	for (i = 0; i < addrcnt; i++) {
		ifindex = 0;

		switch (connp->conn_family) {
		case AF_INET:
			sin4 = (struct sockaddr_in *)addrs + i;
			IN6_INADDR_TO_V4MAPPED(&(sin4->sin_addr), &addr);
			break;
		case AF_INET6:
			sin6 = (struct sockaddr_in6 *)addrs + i;
			addr = sin6->sin6_addr;
			ifindex = sin6->sin6_scope_id;
			break;
		}
		nsp = sctp_saddr_lookup(sctp, &addr, ifindex);
		ASSERT(nsp != NULL);
		nsp->saddr_ipif_delete_pending = 0;
		nsp->saddr_ipif_dontsrc = 0;
	}
	sctp_asconf_destroy(asc);

	return (error);
}
static mblk_t *
sctp_check_addip_addr(sctp_parm_hdr_t *ph, sctp_parm_hdr_t *oph, int *cont,
    uint32_t cid, in6_addr_t *raddr)
{
	uint16_t	atype;
	uint16_t	alen;
	mblk_t		*mp;
	in6_addr_t	addr;
	ipaddr_t	*addr4;

	atype = ntohs(ph->sph_type);
	alen = ntohs(ph->sph_len);

	if (atype != PARM_ADDR4 && atype != PARM_ADDR6) {
		mp = sctp_asconf_adderr(SCTP_ERR_BAD_MANDPARM, oph, cid);
		if (mp == NULL) {
			*cont = -1;
		}
		return (mp);
	}
	if ((atype == PARM_ADDR4 && alen < PARM_ADDR4_LEN) ||
	    (atype == PARM_ADDR6 && alen < PARM_ADDR6_LEN)) {
		mp = sctp_asconf_adderr(SCTP_ERR_BAD_MANDPARM, oph, cid);
		if (mp == NULL) {
			*cont = -1;
		}
		return (mp);
	}

	/* Address parameter is present; extract and screen it */
	if (atype == PARM_ADDR4) {
		addr4 = (ipaddr_t *)(ph + 1);
		IN6_IPADDR_TO_V4MAPPED(*addr4, &addr);

		/* screen XXX loopback to scoping */
		if (*addr4 == 0 || *addr4 == INADDR_BROADCAST ||
		    *addr4 == htonl(INADDR_LOOPBACK) || CLASSD(*addr4)) {
			dprint(1, ("addip: addr not unicast: %x:%x:%x:%x\n",
			    SCTP_PRINTADDR(addr)));
			mp = sctp_asconf_adderr(SCTP_ERR_BAD_MANDPARM, oph,
			    cid);
			if (mp == NULL) {
				*cont = -1;
			}
			return (mp);
		}
		/*
		 * XXX also need to check for subnet
		 * broadcasts. This should probably
		 * wait until we have full access
		 * to the ILL tables.
		 */

	} else {
		bcopy(ph + 1, &addr, sizeof (addr));

		/* screen XXX loopback to scoping */
		if (IN6_IS_ADDR_LINKLOCAL(&addr) ||
		    IN6_IS_ADDR_MULTICAST(&addr) ||
		    IN6_IS_ADDR_LOOPBACK(&addr)) {
			dprint(1, ("addip: addr not unicast: %x:%x:%x:%x\n",
			    SCTP_PRINTADDR(addr)));
			mp = sctp_asconf_adderr(SCTP_ERR_BAD_MANDPARM, oph,
			    cid);
			if (mp == NULL) {
				*cont = -1;
			}
			return (mp);
		}

	}

	/* OK */
	*raddr = addr;
	return (NULL);
}
Exemple #7
0
/* ARGSUSED */
void
dhcp_rebind(iu_tq_t *tqp, void *arg)
{
	dhcp_lease_t	*dlp = arg;
	dhcp_smach_t	*dsmp = dlp->dl_smach;
	int		nlifs;
	dhcp_lif_t	*lif;
	boolean_t	some_valid;
	uint32_t	expiremax;
	DHCPSTATE	oldstate;

	dhcpmsg(MSG_VERBOSE, "dhcp_rebind: T2 timer expired on %s",
	    dsmp->dsm_name);

	dlp->dl_t2.dt_id = -1;

	if ((oldstate = dsmp->dsm_state) == REBINDING) {
		dhcpmsg(MSG_DEBUG, "dhcp_renew: already rebinding");
		release_lease(dlp);
		return;
	}

	/*
	 * Sanity check: don't send packets if we've already expired on all of
	 * the addresses.  We compute the maximum expiration time here, because
	 * it won't matter for v4 (there's only one lease) and for v6 we need
	 * to know when the last lease ages away.
	 */

	some_valid = B_FALSE;
	expiremax = monosec();
	lif = dlp->dl_lifs;
	for (nlifs = dlp->dl_nlifs; nlifs > 0; nlifs--, lif = lif->lif_next) {
		uint32_t expire;

		expire = dsmp->dsm_curstart_monosec + lif->lif_expire.dt_start;
		if (expire > expiremax) {
			expiremax = expire;
			some_valid = B_TRUE;
		}
	}
	if (!some_valid) {
		dhcpmsg(MSG_DEBUG, "dhcp_rebind: all leases expired on %s",
		    dsmp->dsm_name);
		release_lease(dlp);
		return;
	}

	/*
	 * This is our first venture into the REBINDING state, so reset the
	 * server address.  We know the renew timer has already been cancelled
	 * (or we wouldn't be here).
	 */
	if (dsmp->dsm_isv6) {
		dsmp->dsm_server = ipv6_all_dhcp_relay_and_servers;
	} else {
		IN6_IPADDR_TO_V4MAPPED(htonl(INADDR_BROADCAST),
		    &dsmp->dsm_server);
	}

	/* {Bound,Renew}->rebind transitions cannot fail */
	(void) set_smach_state(dsmp, REBINDING);

	/*
	 * If there isn't an async event pending, or if we can cancel the one
	 * that's there, then try to rebind by sending an extension request.
	 * If that fails, we'll clean up when the lease expires.
	 */
	if (!async_cancel(dsmp) || !async_start(dsmp, DHCP_EXTEND, B_FALSE) ||
	    !dhcp_extending(dsmp)) {
		if (monosec() + RETRY_DELAY < expiremax) {
			/*
			 * Try again in RETRY_DELAY seconds; user command
			 * should be gone.
			 */
			init_timer(&dlp->dl_t2, RETRY_DELAY);
			(void) set_smach_state(dsmp, oldstate);
			if (!schedule_lease_timer(dlp, &dlp->dl_t2,
			    dhcp_rebind)) {
				dhcpmsg(MSG_INFO, "dhcp_rebind: unable to "
				    "reschedule rebind around user command on "
				    "%s; lease may expire", dsmp->dsm_name);
			}
		} else {
			dhcpmsg(MSG_WARNING, "dhcp_rebind: user busy on %s; "
			    "will expire", dsmp->dsm_name);
		}
	}
	release_lease(dlp);
}
Exemple #8
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);
}
Exemple #9
0
/*
 * inet_pton: This function takes string format IPv4 or IPv6 address and
 * converts it to binary form. The format of this function corresponds to
 * inet_pton() in the socket library.
 *
 * Return values:
 *  0 invalid IPv4 or IPv6 address
 *  1 successful conversion
 * -1 af is not AF_INET or AF_INET6
 */
int
__inet_pton(int af, char *inp, void *outp, int compat)
{
	int i;
	long byte;
	char *end;

	switch (af) {
	case AF_INET:
		if (str2inet_addr(inp, (ipaddr_t *)outp) != 0) {
			if (!compat)
				*(uint32_t *)outp = htonl(*(uint32_t *)outp);
			return (1);
		} else {
			return (0);
		}
	case AF_INET6: {
		union v6buf_u {
			uint16_t v6words_u[8];
			in6_addr_t v6addr_u;
		} v6buf, *v6outp;
		uint16_t	*dbl_col = NULL;
		char lastbyte = NULL;

		v6outp = (union v6buf_u *)outp;

		if (strchr_w(inp, '.') != NULL) {
			/* v4 mapped or v4 compatable */
			if (strncmp(inp, "::ffff:", 7) == 0) {
				ipaddr_t ipv4_all_zeroes = 0;
				/* mapped - first init prefix and then fill */
				IN6_IPADDR_TO_V4MAPPED(ipv4_all_zeroes,
				    &v6outp->v6addr_u);
				return (str2inet_addr(inp + 7,
				    &(v6outp->v6addr_u.s6_addr32[3])));
			} else if (strncmp(inp, "::", 2) == 0) {
				/* v4 compatable - prefix all zeroes */
				bzero(&v6outp->v6addr_u, sizeof (in6_addr_t));
				return (str2inet_addr(inp + 2,
				    &(v6outp->v6addr_u.s6_addr32[3])));
			}
			return (0);
		}
		for (i = 0; i < 8; i++) {
			int error;
			/*
			 * if ddi_strtol() fails it could be because
			 * the string is "::".  That is valid and
			 * checked for below so just set the value to
			 * 0 and continue.
			 */
			if ((error = ddi_strtol(inp, &end, 16, &byte)) != 0) {
				if (error == ERANGE)
					return (0);
				byte = 0;
			}
			if (byte < 0 || byte > 0x0ffff) {
				return (0);
			}
			if (compat) {
				v6buf.v6words_u[i] = (uint16_t)byte;
			} else {
				v6buf.v6words_u[i] = htons((uint16_t)byte);
			}
			if (*end == NULL || i == 7) {
				inp = end;
				break;
			}
			if (inp == end) {	/* not a number must be */
				if (*inp == ':' &&
				    ((i == 0 && *(inp + 1) == ':') ||
				    lastbyte == ':')) {
					if (dbl_col) {
						return (0);
					}
					if (byte != 0)
						i++;
					dbl_col = &v6buf.v6words_u[i];
					if (i == 0)
						inp++;
				} else if (*inp == NULL || *inp == ' ' ||
				    *inp == '\t') {
					break;
				} else {
					return (0);
				}
			} else {
				inp = end;
			}
			if (*inp != ':') {
				return (0);
			}
			inp++;
			if (*inp == NULL || *inp == ' ' || *inp == '\t') {
				break;
			}
			lastbyte = *inp;
		}
		if (*inp != NULL && *inp != ' ' && *inp != '\t') {
			return (0);
		}
		/*
		 * v6words now contains the bytes we could translate
		 * dbl_col points to the word (should be 0) where
		 * a double colon was found
		 */
		if (i == 7) {
			v6outp->v6addr_u = v6buf.v6addr_u;
		} else {
			int rem;
			int word;
			int next;
			if (dbl_col == NULL) {
				return (0);
			}
			bzero(&v6outp->v6addr_u, sizeof (in6_addr_t));
			rem = dbl_col - &v6buf.v6words_u[0];
			for (next = 0; next < rem; next++) {
				v6outp->v6words_u[next] = v6buf.v6words_u[next];
			}
			next++;	/* skip dbl_col 0 */
			rem = i - rem;
			word = 8 - rem;
			while (rem > 0) {
				v6outp->v6words_u[word] = v6buf.v6words_u[next];
				word++;
				rem--;
				next++;
			}
		}
		return (1);	/* Success */
	}
	}	/* switch */
	return (-1);	/* return -1 for default case */
}
/* Process the COOKIE packet, mp, directed at the listener 'sctp' */
sctp_t *
sctp_conn_request(sctp_t *sctp, mblk_t *mp, uint_t ifindex, uint_t ip_hdr_len,
    sctp_init_chunk_t *iack, ip_recv_attr_t *ira)
{
	sctp_t	*eager;
	ip6_t	*ip6h;
	int	err;
	conn_t	*connp, *econnp;
	sctp_stack_t	*sctps;
	struct sock_proto_props sopp;
	cred_t		*cr;
	pid_t		cpid;
	in6_addr_t	faddr, laddr;
	ip_xmit_attr_t	*ixa;

	/*
	 * No need to check for duplicate as this is the listener
	 * and we are holding the lock.  This means that no new
	 * connection can be created out of it.  And since the
	 * fanout already done cannot find a match, it means that
	 * there is no duplicate.
	 */
	ASSERT(OK_32PTR(mp->b_rptr));

	if ((eager = sctp_create_eager(sctp)) == NULL) {
		return (NULL);
	}

	connp = sctp->sctp_connp;
	sctps = sctp->sctp_sctps;
	econnp = eager->sctp_connp;

	if (connp->conn_policy != NULL) {
		/* Inherit the policy from the listener; use actions from ira */
		if (!ip_ipsec_policy_inherit(econnp, connp, ira)) {
			sctp_close_eager(eager);
			BUMP_MIB(&sctps->sctps_mib, sctpListenDrop);
			return (NULL);
		}
	}

	ip6h = (ip6_t *)mp->b_rptr;
	if (ira->ira_flags & IXAF_IS_IPV4) {
		ipha_t	*ipha;

		ipha = (ipha_t *)ip6h;
		IN6_IPADDR_TO_V4MAPPED(ipha->ipha_dst, &laddr);
		IN6_IPADDR_TO_V4MAPPED(ipha->ipha_src, &faddr);
	} else {
		laddr = ip6h->ip6_dst;
		faddr = ip6h->ip6_src;
	}

	if (ira->ira_flags & IRAF_IPSEC_SECURE) {
		/*
		 * XXX need to fix the cached policy issue here.
		 * We temporarily set the conn_laddr/conn_faddr here so
		 * that IPsec can use it for the latched policy
		 * selector.  This is obvioursly wrong as SCTP can
		 * use different addresses...
		 */
		econnp->conn_laddr_v6 = laddr;
		econnp->conn_faddr_v6 = faddr;
		econnp->conn_saddr_v6 = laddr;
	}
	if (ipsec_conn_cache_policy(econnp,
	    (ira->ira_flags & IRAF_IS_IPV4) != 0) != 0) {
		sctp_close_eager(eager);
		BUMP_MIB(&sctps->sctps_mib, sctpListenDrop);
		return (NULL);
	}

	/* Save for getpeerucred */
	cr = ira->ira_cred;
	cpid = ira->ira_cpid;

	if (is_system_labeled()) {
		ip_xmit_attr_t *ixa = econnp->conn_ixa;

		ASSERT(ira->ira_tsl != NULL);

		/* Discard any old label */
		if (ixa->ixa_free_flags & IXA_FREE_TSL) {
			ASSERT(ixa->ixa_tsl != NULL);
			label_rele(ixa->ixa_tsl);
			ixa->ixa_free_flags &= ~IXA_FREE_TSL;
			ixa->ixa_tsl = NULL;
		}

		if ((connp->conn_mlp_type != mlptSingle ||
		    connp->conn_mac_mode != CONN_MAC_DEFAULT) &&
		    ira->ira_tsl != NULL) {
			/*
			 * If this is an MLP connection or a MAC-Exempt
			 * connection with an unlabeled node, packets are to be
			 * exchanged using the security label of the received
			 * Cookie packet instead of the server application's
			 * label.
			 * tsol_check_dest called from ip_set_destination
			 * might later update TSF_UNLABELED by replacing
			 * ixa_tsl with a new label.
			 */
			label_hold(ira->ira_tsl);
			ip_xmit_attr_replace_tsl(ixa, ira->ira_tsl);
		} else {
			ixa->ixa_tsl = crgetlabel(econnp->conn_cred);
		}
	}

	err = sctp_accept_comm(sctp, eager, mp, ip_hdr_len, iack);
	if (err != 0) {
		sctp_close_eager(eager);
		BUMP_MIB(&sctps->sctps_mib, sctpListenDrop);
		return (NULL);
	}

	ASSERT(eager->sctp_current->ixa != NULL);

	ixa = eager->sctp_current->ixa;
	if (!(ira->ira_flags & IXAF_IS_IPV4)) {
		ASSERT(!(ixa->ixa_flags & IXAF_IS_IPV4));

		if (IN6_IS_ADDR_LINKLOCAL(&ip6h->ip6_src) ||
		    IN6_IS_ADDR_LINKLOCAL(&ip6h->ip6_dst)) {
			eager->sctp_linklocal = 1;

			ixa->ixa_flags |= IXAF_SCOPEID_SET;
			ixa->ixa_scopeid = ifindex;
			econnp->conn_incoming_ifindex = ifindex;
		}
	}

	/*
	 * On a clustered note send this notification to the clustering
	 * subsystem.
	 */
	if (cl_sctp_connect != NULL) {
		uchar_t	*slist;
		uchar_t	*flist;
		size_t	fsize;
		size_t	ssize;

		fsize = sizeof (in6_addr_t) * eager->sctp_nfaddrs;
		ssize = sizeof (in6_addr_t) * eager->sctp_nsaddrs;
		slist = kmem_alloc(ssize, KM_NOSLEEP);
		flist = kmem_alloc(fsize, KM_NOSLEEP);
		if (slist == NULL || flist == NULL) {
			if (slist != NULL)
				kmem_free(slist, ssize);
			if (flist != NULL)
				kmem_free(flist, fsize);
			sctp_close_eager(eager);
			BUMP_MIB(&sctps->sctps_mib, sctpListenDrop);
			SCTP_KSTAT(sctps, sctp_cl_connect);
			return (NULL);
		}
		/* The clustering module frees these list */
		sctp_get_saddr_list(eager, slist, ssize);
		sctp_get_faddr_list(eager, flist, fsize);
		(*cl_sctp_connect)(econnp->conn_family, slist,
		    eager->sctp_nsaddrs, econnp->conn_lport, flist,
		    eager->sctp_nfaddrs, econnp->conn_fport, B_FALSE,
		    (cl_sctp_handle_t)eager);
	}

	/* Connection established, so send up the conn_ind */
	if ((eager->sctp_ulpd = sctp->sctp_ulp_newconn(sctp->sctp_ulpd,
	    (sock_lower_handle_t)eager, NULL, cr, cpid,
	    &eager->sctp_upcalls)) == NULL) {
		sctp_close_eager(eager);
		BUMP_MIB(&sctps->sctps_mib, sctpListenDrop);
		return (NULL);
	}
	ASSERT(SCTP_IS_DETACHED(eager));
	eager->sctp_detached = B_FALSE;
	bzero(&sopp, sizeof (sopp));
	sopp.sopp_flags = SOCKOPT_MAXBLK|SOCKOPT_WROFF;
	sopp.sopp_maxblk = strmsgsz;
	if (econnp->conn_family == AF_INET) {
		sopp.sopp_wroff = sctps->sctps_wroff_xtra +
		    sizeof (sctp_data_hdr_t) + sctp->sctp_hdr_len;
	} else {
		sopp.sopp_wroff = sctps->sctps_wroff_xtra +
		    sizeof (sctp_data_hdr_t) + sctp->sctp_hdr6_len;
	}
	eager->sctp_ulp_prop(eager->sctp_ulpd, &sopp);
	return (eager);
}