示例#1
0
int
sctp_listen(sctp_t *sctp)
{
	sctp_tf_t	*tf;

	RUN_SCTP(sctp);
	/*
	 * TCP handles listen() increasing the backlog, need to check
	 * if it should be handled here too
	 */
	if (sctp->sctp_state > SCTPS_BOUND) {
		WAKE_SCTP(sctp);
		return (EINVAL);
	}

	/* Do an anonymous bind for unbound socket doing listen(). */
	if (sctp->sctp_nsaddrs == 0) {
		struct sockaddr_storage ss;
		int ret;

		bzero(&ss, sizeof (ss));
		ss.ss_family = sctp->sctp_family;

		WAKE_SCTP(sctp);
		if ((ret = sctp_bind(sctp, (struct sockaddr *)&ss,
			sizeof (ss))) != 0)
			return (ret);
		RUN_SCTP(sctp)
	}

	sctp->sctp_state = SCTPS_LISTEN;
	(void) random_get_pseudo_bytes(sctp->sctp_secret, SCTP_SECRET_LEN);
	sctp->sctp_last_secret_update = lbolt64;
	bzero(sctp->sctp_old_secret, SCTP_SECRET_LEN);
	tf = &sctp_listen_fanout[SCTP_LISTEN_HASH(ntohs(sctp->sctp_lport))];
	sctp_listen_hash_insert(tf, sctp);
	WAKE_SCTP(sctp);
	return (0);
}
示例#2
0
/*
 * Remove one or more addresses bound to the sctp_t.
 */
int
sctp_bind_del(sctp_t *sctp, const void *addrs, uint32_t addrcnt,
    boolean_t caller_hold_lock)
{
	int		error = 0;
	boolean_t	do_asconf = B_FALSE;
	uchar_t		*ulist = NULL;
	size_t		usize = 0;

	if (!caller_hold_lock)
		RUN_SCTP(sctp);

	if (sctp->sctp_state > SCTPS_ESTABLISHED) {
		if (!caller_hold_lock)
			WAKE_SCTP(sctp);
		return (EINVAL);
	}
	/*
	 * Fail the remove if we are beyond listen, but can't send this
	 * to the peer.
	 */
	if (sctp->sctp_state > SCTPS_LISTEN) {
		if (!sctp_addip_enabled || !sctp->sctp_understands_asconf ||
		    !sctp->sctp_understands_addip) {
			if (!caller_hold_lock)
				WAKE_SCTP(sctp);
			return (EINVAL);
		}
		do_asconf = B_TRUE;
	}

	/* Can't delete the last address nor all of the addresses */
	if (sctp->sctp_nsaddrs == 1 || addrcnt >= sctp->sctp_nsaddrs) {
		if (!caller_hold_lock)
			WAKE_SCTP(sctp);
		return (EINVAL);
	}

	if (cl_sctp_unlisten != NULL && !do_asconf &&
	    sctp->sctp_state > SCTPS_BOUND) {
		usize = sizeof (in6_addr_t) * addrcnt;
		ulist = kmem_alloc(usize, KM_SLEEP);
	}

	error = sctp_del_ip(sctp, addrs, addrcnt, ulist, usize);
	if (error != 0) {
		if (ulist != NULL)
			kmem_free(ulist, usize);
		if (!caller_hold_lock)
			WAKE_SCTP(sctp);
		return (error);
	}
	/* ulist will be non-NULL only if cl_sctp_unlisten is non-NULL */
	if (ulist != NULL) {
		ASSERT(cl_sctp_unlisten != NULL);
		(*cl_sctp_unlisten)(sctp->sctp_family, ulist, addrcnt,
		    sctp->sctp_lport);
		/* ulist will be freed by the clustering module */
	}
	if (!caller_hold_lock)
		WAKE_SCTP(sctp);
	if (do_asconf)
		sctp_process_sendq(sctp);
	return (error);
}
示例#3
0
/*
 * Add a list of addresses to a sctp_t.
 */
int
sctp_bind_add(sctp_t *sctp, const void *addrs, uint32_t addrcnt,
    boolean_t caller_hold_lock, in_port_t port)
{
	int		err = 0;
	boolean_t	do_asconf = B_FALSE;

	if (!caller_hold_lock)
		RUN_SCTP(sctp);

	if (sctp->sctp_state > SCTPS_ESTABLISHED) {
		if (!caller_hold_lock)
			WAKE_SCTP(sctp);
		return (EINVAL);
	}

	if (sctp->sctp_state > SCTPS_LISTEN) {
		/*
		 * Let's do some checking here rather than undoing the
		 * add later (for these reasons).
		 */
		if (!sctp_addip_enabled || !sctp->sctp_understands_asconf ||
		    !sctp->sctp_understands_addip) {
			if (!caller_hold_lock)
				WAKE_SCTP(sctp);
			return (EINVAL);
		}
		do_asconf = B_TRUE;
	}
	/*
	 * On a clustered node, for an inaddr_any bind, we will pass the list
	 * of all the addresses in the global list, minus any address on the
	 * loopback interface, and expect the clustering susbsystem to give us
	 * the correct list for the 'port'. For explicit binds we give the
	 * list of addresses  and the clustering module validates it for the
	 * 'port'.
	 *
	 * On a non-clustered node, cl_sctp_check_addrs will be NULL and
	 * we proceed as usual.
	 */
	if (cl_sctp_check_addrs != NULL) {
		uchar_t		*addrlist = NULL;
		size_t		size = 0;
		int		unspec = 0;
		boolean_t	do_listen;
		uchar_t		*llist = NULL;
		size_t		lsize = 0;

		/*
		 * If we are adding addresses after listening, but before
		 * an association is established, we need to update the
		 * clustering module with this info.
		 */
		do_listen = !do_asconf && sctp->sctp_state > SCTPS_BOUND &&
		    cl_sctp_listen != NULL;

		err = sctp_get_addrlist(sctp, addrs, &addrcnt, &addrlist,
		    &unspec, &size);
		if (err != 0) {
			ASSERT(addrlist == NULL);
			ASSERT(addrcnt == 0);
			ASSERT(size == 0);
			if (!caller_hold_lock)
				WAKE_SCTP(sctp);
			SCTP_KSTAT(sctp_cl_check_addrs);
			return (err);
		}
		ASSERT(addrlist != NULL);
		(*cl_sctp_check_addrs)(sctp->sctp_family, port, &addrlist,
		    size, &addrcnt, unspec == 1);
		if (addrcnt == 0) {
			/* We free the list */
			kmem_free(addrlist, size);
			if (!caller_hold_lock)
				WAKE_SCTP(sctp);
			return (EINVAL);
		}
		if (do_listen) {
			lsize = sizeof (in6_addr_t) * addrcnt;
			llist = kmem_alloc(lsize, KM_SLEEP);
		}
		err = sctp_valid_addr_list(sctp, addrlist, addrcnt, llist,
		    lsize);
		if (err == 0 && do_listen) {
			(*cl_sctp_listen)(sctp->sctp_family, llist,
			    addrcnt, sctp->sctp_lport);
			/* list will be freed by the clustering module */
		} else if (err != 0 && llist != NULL) {
			kmem_free(llist, lsize);
		}
		/* free the list we allocated */
		kmem_free(addrlist, size);
	} else {
		err = sctp_valid_addr_list(sctp, addrs, addrcnt, NULL, 0);
	}
	if (err != 0) {
		if (!caller_hold_lock)
			WAKE_SCTP(sctp);
		return (err);
	}
	/* Need to send  ASCONF messages */
	if (do_asconf) {
		err = sctp_add_ip(sctp, addrs, addrcnt);
		if (err != 0) {
			sctp_del_saddr_list(sctp, addrs, addrcnt, B_FALSE);
			if (!caller_hold_lock)
				WAKE_SCTP(sctp);
			return (err);
		}
	}
	if (!caller_hold_lock)
		WAKE_SCTP(sctp);
	if (do_asconf)
		sctp_process_sendq(sctp);
	return (0);
}
示例#4
0
/*
 * Bind the sctp_t to a sockaddr, which includes an address and other
 * information, such as port or flowinfo.
 */
int
sctp_bind(sctp_t *sctp, struct sockaddr *sa, socklen_t len)
{
	int		user_specified;
	boolean_t	bind_to_req_port_only;
	in_port_t	requested_port;
	in_port_t	allocated_port;
	int		err = 0;

	ASSERT(sctp != NULL);
	ASSERT(sa);

	RUN_SCTP(sctp);

	if (sctp->sctp_state > SCTPS_BOUND) {
		err = EINVAL;
		goto done;
	}

	switch (sa->sa_family) {
	case AF_INET:
		if (len < sizeof (struct sockaddr_in) ||
		    sctp->sctp_family == AF_INET6) {
			err = EINVAL;
			goto done;
		}
		requested_port = ntohs(((struct sockaddr_in *)sa)->sin_port);
		break;
	case AF_INET6:
		if (len < sizeof (struct sockaddr_in6) ||
		    sctp->sctp_family == AF_INET) {
			err = EINVAL;
			goto done;
		}
		requested_port = ntohs(((struct sockaddr_in6 *)sa)->sin6_port);
		/* Set the flowinfo. */
		sctp->sctp_ip6h->ip6_vcf =
		    (IPV6_DEFAULT_VERS_AND_FLOW & IPV6_VERS_AND_FLOW_MASK) |
		    (((struct sockaddr_in6 *)sa)->sin6_flowinfo &
		    ~IPV6_VERS_AND_FLOW_MASK);
		break;
	default:
		err = EAFNOSUPPORT;
		goto done;
	}
	bind_to_req_port_only = requested_port == 0 ? B_FALSE : B_TRUE;

	err = sctp_select_port(sctp, &requested_port, &user_specified);
	if (err != 0)
		goto done;

	if ((err = sctp_bind_add(sctp, sa, 1, B_TRUE,
	    user_specified == 1 ? htons(requested_port) : 0)) != 0) {
		goto done;
	}
	err = sctp_bindi(sctp, requested_port, bind_to_req_port_only,
	    user_specified, &allocated_port);
	if (err != 0) {
		sctp_free_saddrs(sctp);
	} else {
		ASSERT(sctp->sctp_state == SCTPS_BOUND);
	}
done:
	WAKE_SCTP(sctp);
	return (err);
}
示例#5
0
/*
 * Exported routine for extracting active SCTP associations.
 * Like TCP, we terminate the walk if the callback returns non-zero.
 */
int
cl_sctp_walk_list(int (*cl_callback)(cl_sctp_info_t *, void *), void *arg,
    boolean_t cansleep)
{
	sctp_t		*sctp;
	sctp_t		*sctp_prev;
	cl_sctp_info_t	cl_sctpi;
	uchar_t		*slist;
	uchar_t		*flist;

	sctp = gsctp;
	sctp_prev = NULL;
	mutex_enter(&sctp_g_lock);
	while (sctp != NULL) {
		size_t	ssize;
		size_t	fsize;

		mutex_enter(&sctp->sctp_reflock);
		if (sctp->sctp_condemned || sctp->sctp_state <= SCTPS_LISTEN) {
			mutex_exit(&sctp->sctp_reflock);
			sctp = list_next(&sctp_g_list, sctp);
			continue;
		}
		sctp->sctp_refcnt++;
		mutex_exit(&sctp->sctp_reflock);
		mutex_exit(&sctp_g_lock);
		if (sctp_prev != NULL)
			SCTP_REFRELE(sctp_prev);
		RUN_SCTP(sctp);
		ssize = sizeof (in6_addr_t) * sctp->sctp_nsaddrs;
		fsize = sizeof (in6_addr_t) * sctp->sctp_nfaddrs;

		slist = kmem_alloc(ssize, cansleep ? KM_SLEEP : KM_NOSLEEP);
		flist = kmem_alloc(fsize, cansleep ? KM_SLEEP : KM_NOSLEEP);
		if (slist == NULL || flist == NULL) {
			WAKE_SCTP(sctp);
			if (slist != NULL)
				kmem_free(slist, ssize);
			if (flist != NULL)
				kmem_free(flist, fsize);
			SCTP_REFRELE(sctp);
			return (1);
		}
		cl_sctpi.cl_sctpi_version = CL_SCTPI_V1;
		sctp_get_saddr_list(sctp, slist, ssize);
		sctp_get_faddr_list(sctp, flist, fsize);
		cl_sctpi.cl_sctpi_nladdr = sctp->sctp_nsaddrs;
		cl_sctpi.cl_sctpi_nfaddr = sctp->sctp_nfaddrs;
		cl_sctpi.cl_sctpi_family = sctp->sctp_family;
		cl_sctpi.cl_sctpi_ipversion = sctp->sctp_ipversion;
		cl_sctpi.cl_sctpi_state = sctp->sctp_state;
		cl_sctpi.cl_sctpi_lport = sctp->sctp_lport;
		cl_sctpi.cl_sctpi_fport = sctp->sctp_fport;
		cl_sctpi.cl_sctpi_handle = (cl_sctp_handle_t)sctp;
		WAKE_SCTP(sctp);
		cl_sctpi.cl_sctpi_laddrp = slist;
		cl_sctpi.cl_sctpi_faddrp = flist;
		if ((*cl_callback)(&cl_sctpi, arg) != 0) {
			kmem_free(slist, ssize);
			kmem_free(flist, fsize);
			SCTP_REFRELE(sctp);
			return (1);
		}
		/* list will be freed by cl_callback */
		sctp_prev = sctp;
		mutex_enter(&sctp_g_lock);
		sctp = list_next(&sctp_g_list, sctp);
	}
	mutex_exit(&sctp_g_lock);
	if (sctp_prev != NULL)
		SCTP_REFRELE(sctp_prev);
	return (0);
}
示例#6
0
/*
 * Walk the SCTP global list and refrele the ire for this ipif
 * This is called when an address goes down, so that we release any reference
 * to the ire associated with this address. Additionally, for any SCTP if
 * this was the only/last address in its source list, we don't kill the
 * assoc., if there is no address added subsequently, or if this does not
 * come up, then the assoc. will die a natural death (i.e. timeout).
 */
void
sctp_ire_cache_flush(ipif_t *ipif)
{
	sctp_t			*sctp;
	sctp_t			*sctp_prev = NULL;
	sctp_faddr_t		*fp;
	conn_t			*connp;
	ire_t			*ire;

	sctp = gsctp;
	mutex_enter(&sctp_g_lock);
	while (sctp != NULL) {
		mutex_enter(&sctp->sctp_reflock);
		if (sctp->sctp_condemned) {
			mutex_exit(&sctp->sctp_reflock);
			sctp = list_next(&sctp_g_list, sctp);
			continue;
		}
		sctp->sctp_refcnt++;
		mutex_exit(&sctp->sctp_reflock);
		mutex_exit(&sctp_g_lock);
		if (sctp_prev != NULL)
			SCTP_REFRELE(sctp_prev);

		RUN_SCTP(sctp);
		connp = sctp->sctp_connp;
		mutex_enter(&connp->conn_lock);
		ire = connp->conn_ire_cache;
		if (ire != NULL && ire->ire_ipif == ipif) {
			connp->conn_ire_cache = NULL;
			mutex_exit(&connp->conn_lock);
			IRE_REFRELE_NOTR(ire);
		} else {
			mutex_exit(&connp->conn_lock);
		}
		/* check for ires cached in faddr */
		for (fp = sctp->sctp_faddrs; fp != NULL; fp = fp->next) {
			/*
			 * If this ipif is being used as the source address
			 * we need to update it as well, else we will end
			 * up using the dead source address.
			 */
			ire = fp->ire;
			if (ire != NULL && ire->ire_ipif == ipif) {
				fp->ire = NULL;
				IRE_REFRELE_NOTR(ire);
			}
			/*
			 * This may result in setting the fp as unreachable,
			 * i.e. if all the source addresses are down. In
			 * that case the assoc. would timeout.
			 */
			if (IN6_ARE_ADDR_EQUAL(&ipif->ipif_v6lcl_addr,
			    &fp->saddr)) {
				sctp_set_saddr(sctp, fp);
				if (fp == sctp->sctp_current &&
				    fp->state != SCTP_FADDRS_UNREACH) {
					sctp_set_faddr_current(sctp, fp);
				}
			}
		}
		WAKE_SCTP(sctp);
		sctp_prev = sctp;
		mutex_enter(&sctp_g_lock);
		sctp = list_next(&sctp_g_list, sctp);
	}
	mutex_exit(&sctp_g_lock);
	if (sctp_prev != NULL)
		SCTP_REFRELE(sctp_prev);
}
示例#7
0
/*
 * Bind the sctp_t to a sockaddr, which includes an address and other
 * information, such as port or flowinfo.
 */
int
sctp_bind(sctp_t *sctp, struct sockaddr *sa, socklen_t len)
{
	int		user_specified;
	boolean_t	bind_to_req_port_only;
	in_port_t	requested_port;
	in_port_t	allocated_port;
	int		err = 0;
	conn_t		*connp = sctp->sctp_connp;
	uint_t		scope_id;
	sin_t		*sin;
	sin6_t		*sin6;

	ASSERT(sctp != NULL);

	RUN_SCTP(sctp);

	if ((sctp->sctp_state >= SCTPS_BOUND) ||
	    (sctp->sctp_connp->conn_state_flags & CONN_CLOSING) ||
	    (sa == NULL || len == 0)) {
		/*
		 * Multiple binds not allowed for any SCTP socket
		 * Also binding with null address is not supported.
		 */
		err = EINVAL;
		goto done;
	}

	switch (sa->sa_family) {
	case AF_INET:
		sin = (sin_t *)sa;
		if (len < sizeof (struct sockaddr_in) ||
		    connp->conn_family == AF_INET6) {
			err = EINVAL;
			goto done;
		}
		requested_port = ntohs(sin->sin_port);
		break;
	case AF_INET6:
		sin6 = (sin6_t *)sa;
		if (len < sizeof (struct sockaddr_in6) ||
		    connp->conn_family == AF_INET) {
			err = EINVAL;
			goto done;
		}
		requested_port = ntohs(sin6->sin6_port);
		/* Set the flowinfo. */
		connp->conn_flowinfo =
		    sin6->sin6_flowinfo & ~IPV6_VERS_AND_FLOW_MASK;

		scope_id = sin6->sin6_scope_id;
		if (scope_id != 0 && IN6_IS_ADDR_LINKSCOPE(&sin6->sin6_addr)) {
			connp->conn_ixa->ixa_flags |= IXAF_SCOPEID_SET;
			connp->conn_ixa->ixa_scopeid = scope_id;
			connp->conn_incoming_ifindex = scope_id;
		} else {
			connp->conn_ixa->ixa_flags &= ~IXAF_SCOPEID_SET;
			connp->conn_incoming_ifindex = connp->conn_bound_if;
		}
		break;
	default:
		err = EAFNOSUPPORT;
		goto done;
	}
	bind_to_req_port_only = requested_port == 0 ? B_FALSE : B_TRUE;

	err = sctp_select_port(sctp, &requested_port, &user_specified);
	if (err != 0)
		goto done;

	if ((err = sctp_bind_add(sctp, sa, 1, B_TRUE,
	    user_specified == 1 ? htons(requested_port) : 0)) != 0) {
		goto done;
	}
	err = sctp_bindi(sctp, requested_port, bind_to_req_port_only,
	    user_specified, &allocated_port);
	if (err != 0) {
		sctp_free_saddrs(sctp);
	} else {
		ASSERT(sctp->sctp_state == SCTPS_BOUND);
	}
done:
	WAKE_SCTP(sctp);
	return (err);
}
示例#8
0
int
sctp_listen(sctp_t *sctp)
{
	sctp_tf_t	*tf;
	sctp_stack_t	*sctps = sctp->sctp_sctps;
	conn_t		*connp = sctp->sctp_connp;

	RUN_SCTP(sctp);
	/*
	 * TCP handles listen() increasing the backlog, need to check
	 * if it should be handled here too
	 */
	if (sctp->sctp_state > SCTPS_BOUND ||
	    (sctp->sctp_connp->conn_state_flags & CONN_CLOSING)) {
		WAKE_SCTP(sctp);
		return (EINVAL);
	}

	/* Do an anonymous bind for unbound socket doing listen(). */
	if (sctp->sctp_nsaddrs == 0) {
		struct sockaddr_storage ss;
		int ret;

		bzero(&ss, sizeof (ss));
		ss.ss_family = connp->conn_family;

		WAKE_SCTP(sctp);
		if ((ret = sctp_bind(sctp, (struct sockaddr *)&ss,
		    sizeof (ss))) != 0)
			return (ret);
		RUN_SCTP(sctp)
	}

	/* Cache things in the ixa without any refhold */
	ASSERT(!(connp->conn_ixa->ixa_free_flags & IXA_FREE_CRED));
	connp->conn_ixa->ixa_cred = connp->conn_cred;
	connp->conn_ixa->ixa_cpid = connp->conn_cpid;
	if (is_system_labeled())
		connp->conn_ixa->ixa_tsl = crgetlabel(connp->conn_cred);

	sctp->sctp_state = SCTPS_LISTEN;
	(void) random_get_pseudo_bytes(sctp->sctp_secret, SCTP_SECRET_LEN);
	sctp->sctp_last_secret_update = ddi_get_lbolt64();
	bzero(sctp->sctp_old_secret, SCTP_SECRET_LEN);

	/*
	 * If there is an association limit, allocate and initialize
	 * the counter struct.  Note that since listen can be called
	 * multiple times, the struct may have been allready allocated.
	 */
	if (!list_is_empty(&sctps->sctps_listener_conf) &&
	    sctp->sctp_listen_cnt == NULL) {
		sctp_listen_cnt_t *slc;
		uint32_t ratio;

		ratio = sctp_find_listener_conf(sctps,
		    ntohs(connp->conn_lport));
		if (ratio != 0) {
			uint32_t mem_ratio, tot_buf;

			slc = kmem_alloc(sizeof (sctp_listen_cnt_t), KM_SLEEP);
			/*
			 * Calculate the connection limit based on
			 * the configured ratio and maxusers.  Maxusers
			 * are calculated based on memory size,
			 * ~ 1 user per MB.  Note that the conn_rcvbuf
			 * and conn_sndbuf may change after a
			 * connection is accepted.  So what we have
			 * is only an approximation.
			 */
			if ((tot_buf = connp->conn_rcvbuf +
			    connp->conn_sndbuf) < MB) {
				mem_ratio = MB / tot_buf;
				slc->slc_max = maxusers / ratio * mem_ratio;
			} else {
				mem_ratio = tot_buf / MB;
				slc->slc_max = maxusers / ratio / mem_ratio;
			}
			/* At least we should allow some associations! */
			if (slc->slc_max < sctp_min_assoc_listener)
				slc->slc_max = sctp_min_assoc_listener;
			slc->slc_cnt = 1;
			slc->slc_drop = 0;
			sctp->sctp_listen_cnt = slc;
		}
	}


	tf = &sctps->sctps_listen_fanout[SCTP_LISTEN_HASH(
	    ntohs(connp->conn_lport))];
	sctp_listen_hash_insert(tf, sctp);

	WAKE_SCTP(sctp);
	return (0);
}
/*
 * Common accept code.  Called by sctp_conn_request.
 * cr_pkt is the INIT / INIT ACK packet.
 */
static int
sctp_accept_comm(sctp_t *listener, sctp_t *acceptor, mblk_t *cr_pkt,
    uint_t ip_hdr_len, sctp_init_chunk_t *iack)
{

	sctp_hdr_t		*sctph;
	sctp_chunk_hdr_t	*ich;
	sctp_init_chunk_t	*init;
	int			err;
	uint_t			sctp_options;
	conn_t			*aconnp;
	conn_t			*lconnp;
	sctp_stack_t	*sctps = listener->sctp_sctps;

	sctph = (sctp_hdr_t *)(cr_pkt->b_rptr + ip_hdr_len);
	ASSERT(OK_32PTR(sctph));

	aconnp = acceptor->sctp_connp;
	lconnp = listener->sctp_connp;
	aconnp->conn_lport = lconnp->conn_lport;
	aconnp->conn_fport = sctph->sh_sport;

	ich = (sctp_chunk_hdr_t *)(iack + 1);
	init = (sctp_init_chunk_t *)(ich + 1);

	/* acceptor isn't in any fanouts yet, so don't need to hold locks */
	ASSERT(acceptor->sctp_faddrs == NULL);
	err = sctp_get_addrparams(acceptor, listener, cr_pkt, ich,
	    &sctp_options);
	if (err != 0)
		return (err);

	if ((err = sctp_set_hdraddrs(acceptor)) != 0)
		return (err);

	if ((err = sctp_build_hdrs(acceptor, KM_NOSLEEP)) != 0)
		return (err);

	if ((sctp_options & SCTP_PRSCTP_OPTION) &&
	    listener->sctp_prsctp_aware && sctps->sctps_prsctp_enabled) {
		acceptor->sctp_prsctp_aware = B_TRUE;
	} else {
		acceptor->sctp_prsctp_aware = B_FALSE;
	}

	/* Get  initial TSNs */
	acceptor->sctp_ltsn = ntohl(iack->sic_inittsn);
	acceptor->sctp_recovery_tsn = acceptor->sctp_lastack_rxd =
	    acceptor->sctp_ltsn - 1;
	acceptor->sctp_adv_pap = acceptor->sctp_lastack_rxd;
	/* Serial numbers are initialized to the same value as the TSNs */
	acceptor->sctp_lcsn = acceptor->sctp_ltsn;

	if (!sctp_initialize_params(acceptor, init, iack))
		return (ENOMEM);

	/*
	 * Copy sctp_secret from the listener in case we need to validate
	 * a possibly delayed cookie.
	 */
	bcopy(listener->sctp_secret, acceptor->sctp_secret, SCTP_SECRET_LEN);
	bcopy(listener->sctp_old_secret, acceptor->sctp_old_secret,
	    SCTP_SECRET_LEN);
	acceptor->sctp_last_secret_update = ddi_get_lbolt64();

	/*
	 * After acceptor is inserted in the hash list, it can be found.
	 * So we need to lock it here.
	 */
	RUN_SCTP(acceptor);

	sctp_conn_hash_insert(&sctps->sctps_conn_fanout[
	    SCTP_CONN_HASH(sctps, aconnp->conn_ports)], acceptor, 0);
	sctp_bind_hash_insert(&sctps->sctps_bind_fanout[
	    SCTP_BIND_HASH(ntohs(aconnp->conn_lport))], acceptor, 0);

	/*
	 * No need to check for multicast destination since ip will only pass
	 * up multicasts to those that have expressed interest
	 * TODO: what about rejecting broadcasts?
	 * Also check that source is not a multicast or broadcast address.
	 */
	/* XXXSCTP */
	acceptor->sctp_state = SCTPS_ESTABLISHED;
	acceptor->sctp_assoc_start_time = (uint32_t)ddi_get_lbolt();
	/*
	 * listener->sctp_rwnd should be the default window size or a
	 * window size changed via SO_RCVBUF option.
	 */
	acceptor->sctp_rwnd = listener->sctp_rwnd;
	acceptor->sctp_irwnd = acceptor->sctp_rwnd;
	acceptor->sctp_pd_point = acceptor->sctp_rwnd;
	acceptor->sctp_upcalls = listener->sctp_upcalls;

	return (0);
}
示例#10
0
/*
 * Connect to a peer - this function inserts the sctp in the
 * bind and conn fanouts, sends the INIT, and replies to the client
 * with an OK ack.
 */
int
sctp_connect(sctp_t *sctp, const struct sockaddr *dst, uint32_t addrlen,
    cred_t *cr, pid_t pid)
{
	sin_t		*sin;
	sin6_t		*sin6;
	in6_addr_t	dstaddr;
	in_port_t	dstport;
	mblk_t		*initmp;
	sctp_tf_t	*tbf;
	sctp_t		*lsctp;
	char		buf[INET6_ADDRSTRLEN];
	int		sleep = sctp->sctp_cansleep ? KM_SLEEP : KM_NOSLEEP;
	int		err;
	sctp_faddr_t	*cur_fp;
	sctp_stack_t	*sctps = sctp->sctp_sctps;
	conn_t		*connp = sctp->sctp_connp;
	uint_t		scope_id = 0;
	ip_xmit_attr_t	*ixa;

	/*
	 * Determine packet type based on type of address passed in
	 * the request should contain an IPv4 or IPv6 address.
	 * Make sure that address family matches the type of
	 * family of the address passed down.
	 */
	if (addrlen < sizeof (sin_t)) {
		return (EINVAL);
	}
	switch (dst->sa_family) {
	case AF_INET:
		sin = (sin_t *)dst;

		/* Check for attempt to connect to non-unicast */
		if (CLASSD(sin->sin_addr.s_addr) ||
		    (sin->sin_addr.s_addr == INADDR_BROADCAST)) {
			ip0dbg(("sctp_connect: non-unicast\n"));
			return (EINVAL);
		}
		if (connp->conn_ipv6_v6only)
			return (EAFNOSUPPORT);

		/* convert to v6 mapped */
		/* Check for attempt to connect to INADDR_ANY */
		if (sin->sin_addr.s_addr == INADDR_ANY)  {
			struct in_addr v4_addr;
			/*
			 * SunOS 4.x and 4.3 BSD allow an application
			 * to connect a TCP socket to INADDR_ANY.
			 * When they do this, the kernel picks the
			 * address of one interface and uses it
			 * instead.  The kernel usually ends up
			 * picking the address of the loopback
			 * interface.  This is an undocumented feature.
			 * However, we provide the same thing here
			 * in case any TCP apps that use this feature
			 * are being ported to SCTP...
			 */
			v4_addr.s_addr = htonl(INADDR_LOOPBACK);
			IN6_INADDR_TO_V4MAPPED(&v4_addr, &dstaddr);
		} else {
			IN6_INADDR_TO_V4MAPPED(&sin->sin_addr, &dstaddr);
		}
		dstport = sin->sin_port;
		break;
	case AF_INET6:
		sin6 = (sin6_t *)dst;
		/* Check for attempt to connect to non-unicast. */
		if ((addrlen < sizeof (sin6_t)) ||
		    IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) {
			ip0dbg(("sctp_connect: non-unicast\n"));
			return (EINVAL);
		}
		if (connp->conn_ipv6_v6only &&
		    IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
			return (EAFNOSUPPORT);
		}
		/* check for attempt to connect to unspec */
		if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
			dstaddr = ipv6_loopback;
		} else {
			dstaddr = sin6->sin6_addr;
			if (IN6_IS_ADDR_LINKLOCAL(&dstaddr)) {
				sctp->sctp_linklocal = 1;
				scope_id = sin6->sin6_scope_id;
			}
		}
		dstport = sin6->sin6_port;
		connp->conn_flowinfo = sin6->sin6_flowinfo;
		break;
	default:
		dprint(1, ("sctp_connect: unknown family %d\n",
		    dst->sa_family));
		return (EAFNOSUPPORT);
	}

	(void) inet_ntop(AF_INET6, &dstaddr, buf, sizeof (buf));
	dprint(1, ("sctp_connect: attempting connect to %s...\n", buf));

	RUN_SCTP(sctp);

	if (connp->conn_family != dst->sa_family ||
	    (connp->conn_state_flags & CONN_CLOSING)) {
		WAKE_SCTP(sctp);
		return (EINVAL);
	}

	/* We update our cred/cpid based on the caller of connect */
	if (connp->conn_cred != cr) {
		crhold(cr);
		crfree(connp->conn_cred);
		connp->conn_cred = cr;
	}
	connp->conn_cpid = pid;

	/* Cache things in conn_ixa without any refhold */
	ixa = connp->conn_ixa;
	ixa->ixa_cred = cr;
	ixa->ixa_cpid = pid;
	if (is_system_labeled()) {
		/* We need to restart with a label based on the cred */
		ip_xmit_attr_restore_tsl(ixa, ixa->ixa_cred);
	}

	switch (sctp->sctp_state) {
	case SCTPS_IDLE: {
		struct sockaddr_storage	ss;

		/*
		 * We support a quick connect capability here, allowing
		 * clients to transition directly from IDLE to COOKIE_WAIT.
		 * sctp_bindi will pick an unused port, insert the connection
		 * in the bind hash and transition to BOUND state. SCTP
		 * picks and uses what it considers the optimal local address
		 * set (just like specifiying INADDR_ANY to bind()).
		 */
		dprint(1, ("sctp_connect: idle, attempting bind...\n"));
		ASSERT(sctp->sctp_nsaddrs == 0);

		bzero(&ss, sizeof (ss));
		ss.ss_family = connp->conn_family;
		WAKE_SCTP(sctp);
		if ((err = sctp_bind(sctp, (struct sockaddr *)&ss,
		    sizeof (ss))) != 0) {
			return (err);
		}
		RUN_SCTP(sctp);
		/* FALLTHRU */
	}

	case SCTPS_BOUND:
		ASSERT(sctp->sctp_nsaddrs > 0);

		/* do the connect */
		/* XXX check for attempt to connect to self */
		connp->conn_fport = dstport;

		ASSERT(sctp->sctp_iphc);
		ASSERT(sctp->sctp_iphc6);

		/*
		 * Don't allow this connection to completely duplicate
		 * an existing connection.
		 *
		 * Ensure that the duplicate check and insertion is atomic.
		 */
		sctp_conn_hash_remove(sctp);
		tbf = &sctps->sctps_conn_fanout[SCTP_CONN_HASH(sctps,
		    connp->conn_ports)];
		mutex_enter(&tbf->tf_lock);
		lsctp = sctp_lookup(sctp, &dstaddr, tbf, &connp->conn_ports,
		    SCTPS_COOKIE_WAIT);
		if (lsctp != NULL) {
			/* found a duplicate connection */
			mutex_exit(&tbf->tf_lock);
			SCTP_REFRELE(lsctp);
			WAKE_SCTP(sctp);
			return (EADDRINUSE);
		}

		/*
		 * OK; set up the peer addr (this may grow after we get
		 * the INIT ACK from the peer with additional addresses).
		 */
		if ((err = sctp_add_faddr(sctp, &dstaddr, sleep,
		    B_FALSE)) != 0) {
			mutex_exit(&tbf->tf_lock);
			WAKE_SCTP(sctp);
			return (err);
		}
		cur_fp = sctp->sctp_faddrs;
		ASSERT(cur_fp->ixa != NULL);

		/* No valid src addr, return. */
		if (cur_fp->state == SCTP_FADDRS_UNREACH) {
			mutex_exit(&tbf->tf_lock);
			WAKE_SCTP(sctp);
			return (EADDRNOTAVAIL);
		}

		sctp->sctp_primary = cur_fp;
		sctp->sctp_current = cur_fp;
		sctp->sctp_mss = cur_fp->sfa_pmss;
		sctp_conn_hash_insert(tbf, sctp, 1);
		mutex_exit(&tbf->tf_lock);

		ixa = cur_fp->ixa;
		ASSERT(ixa->ixa_cred != NULL);

		if (scope_id != 0) {
			ixa->ixa_flags |= IXAF_SCOPEID_SET;
			ixa->ixa_scopeid = scope_id;
		} else {
			ixa->ixa_flags &= ~IXAF_SCOPEID_SET;
		}

		/* initialize composite headers */
		if ((err = sctp_set_hdraddrs(sctp)) != 0) {
			sctp_conn_hash_remove(sctp);
			WAKE_SCTP(sctp);
			return (err);
		}

		if ((err = sctp_build_hdrs(sctp, KM_SLEEP)) != 0) {
			sctp_conn_hash_remove(sctp);
			WAKE_SCTP(sctp);
			return (err);
		}

		/*
		 * Turn off the don't fragment bit on the (only) faddr,
		 * so that if one of the messages exchanged during the
		 * initialization sequence exceeds the path mtu, it
		 * at least has a chance to get there. SCTP does no
		 * fragmentation of initialization messages.  The DF bit
		 * will be turned on again in sctp_send_cookie_echo()
		 * (but the cookie echo will still be sent with the df bit
		 * off).
		 */
		cur_fp->df = B_FALSE;

		/* Mark this address as alive */
		cur_fp->state = SCTP_FADDRS_ALIVE;

		/* Send the INIT to the peer */
		SCTP_FADDR_TIMER_RESTART(sctp, cur_fp, cur_fp->rto);
		sctp->sctp_state = SCTPS_COOKIE_WAIT;
		/*
		 * sctp_init_mp() could result in modifying the source
		 * address list, so take the hash lock.
		 */
		mutex_enter(&tbf->tf_lock);
		initmp = sctp_init_mp(sctp, cur_fp);
		if (initmp == NULL) {
			mutex_exit(&tbf->tf_lock);
			/*
			 * It may happen that all the source addresses
			 * (loopback/link local) are removed.  In that case,
			 * faile the connect.
			 */
			if (sctp->sctp_nsaddrs == 0) {
				sctp_conn_hash_remove(sctp);
				SCTP_FADDR_TIMER_STOP(cur_fp);
				WAKE_SCTP(sctp);
				return (EADDRNOTAVAIL);
			}

			/* Otherwise, let the retransmission timer retry */
			WAKE_SCTP(sctp);
			goto notify_ulp;
		}
		mutex_exit(&tbf->tf_lock);

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

			fsize = sizeof (in6_addr_t) * sctp->sctp_nfaddrs;
			ssize = sizeof (in6_addr_t) * sctp->sctp_nsaddrs;
			slist = kmem_alloc(ssize, KM_SLEEP);
			flist = kmem_alloc(fsize, KM_SLEEP);
			/* The clustering module frees the lists */
			sctp_get_saddr_list(sctp, slist, ssize);
			sctp_get_faddr_list(sctp, flist, fsize);
			(*cl_sctp_connect)(connp->conn_family, slist,
			    sctp->sctp_nsaddrs, connp->conn_lport,
			    flist, sctp->sctp_nfaddrs, connp->conn_fport,
			    B_TRUE, (cl_sctp_handle_t)sctp);
		}
		ASSERT(ixa->ixa_cred != NULL);
		ASSERT(ixa->ixa_ire != NULL);

		(void) conn_ip_output(initmp, ixa);
		BUMP_LOCAL(sctp->sctp_opkts);
		WAKE_SCTP(sctp);

notify_ulp:
		sctp_set_ulp_prop(sctp);

		return (0);
	default:
		ip0dbg(("sctp_connect: invalid state. %d\n", sctp->sctp_state));
		WAKE_SCTP(sctp);
		return (EINVAL);
	}
}