Ejemplo n.º 1
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);
}
Ejemplo n.º 2
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);
}