예제 #1
0
void
sctp_zap_addrs(sctp_t *sctp)
{
	sctp_zap_faddrs(sctp, 0);
	sctp_free_saddrs(sctp);
}
예제 #2
0
파일: sctp_bind.c 프로젝트: andreiw/polaris
/*
 * 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);
}
예제 #3
0
/*
 * 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);
}
예제 #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;
	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);
}