⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 socket.c

📁 在linux环境下的流控制传输协议(sctp)的源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
}/* Add a list of addresses as bind addresses to local endpoint or * association. * * Basically run through each address specified in the addrs/addrcnt * array/length pair, determine if it is IPv6 or IPv4 and call * sctp_do_bind() on it. * * If any of them fails, then the operation will be reversed and the * ones that were added will be removed. * * Only sctp_setsockopt_bindx() is supposed to call this function. */static int sctp_bindx_add(struct sock *sk, struct sockaddr *addrs, int addrcnt){	int cnt;	int retval = 0;	void *addr_buf;	struct sockaddr *sa_addr;	struct sctp_af *af;	SCTP_DEBUG_PRINTK("sctp_bindx_add (sk: %p, addrs: %p, addrcnt: %d)\n",			  sk, addrs, addrcnt);	addr_buf = addrs;	for (cnt = 0; cnt < addrcnt; cnt++) {		/* The list may contain either IPv4 or IPv6 address;		 * determine the address length for walking thru the list.		 */		sa_addr = (struct sockaddr *)addr_buf;		af = sctp_get_af_specific(sa_addr->sa_family);		if (!af) {			retval = -EINVAL;			goto err_bindx_add;		}		retval = sctp_do_bind(sk, (union sctp_addr *)sa_addr,				      af->sockaddr_len);		addr_buf += af->sockaddr_len;err_bindx_add:		if (retval < 0) {			/* Failed. Cleanup the ones that have been added */			if (cnt > 0)				sctp_bindx_rem(sk, addrs, cnt);			return retval;		}	}	return retval;}/* Send an ASCONF chunk with Add IP address parameters to all the peers of the * associations that are part of the endpoint indicating that a list of local * addresses are added to the endpoint. * * If any of the addresses is already in the bind address list of the * association, we do not send the chunk for that association.  But it will not * affect other associations. * * Only sctp_setsockopt_bindx() is supposed to call this function. */static int sctp_send_asconf_add_ip(struct sock		*sk,				   struct sockaddr	*addrs,				   int 			addrcnt){	struct sctp_sock		*sp;	struct sctp_endpoint		*ep;	struct sctp_association		*asoc;	struct sctp_bind_addr		*bp;	struct sctp_chunk		*chunk;	struct sctp_sockaddr_entry	*laddr;	union sctp_addr			*addr;	union sctp_addr			saveaddr;	void				*addr_buf;	struct sctp_af			*af;	struct list_head		*pos;	struct list_head		*p;	int 				i;	int 				retval = 0;	if (!sctp_addip_enable)		return retval;	sp = sctp_sk(sk);	ep = sp->ep;	SCTP_DEBUG_PRINTK("%s: (sk: %p, addrs: %p, addrcnt: %d)\n",			  __FUNCTION__, sk, addrs, addrcnt);	list_for_each(pos, &ep->asocs) {		asoc = list_entry(pos, struct sctp_association, asocs);		if (!asoc->peer.asconf_capable)			continue;		if (asoc->peer.addip_disabled_mask & SCTP_PARAM_ADD_IP)			continue;		if (!sctp_state(asoc, ESTABLISHED))			continue;		/* Check if any address in the packed array of addresses is		 * in the bind address list of the association. If so,		 * do not send the asconf chunk to its peer, but continue with		 * other associations.		 */		addr_buf = addrs;		for (i = 0; i < addrcnt; i++) {			addr = (union sctp_addr *)addr_buf;			af = sctp_get_af_specific(addr->v4.sin_family);			if (!af) {				retval = -EINVAL;				goto out;			}			if (sctp_assoc_lookup_laddr(asoc, addr))				break;			addr_buf += af->sockaddr_len;		}		if (i < addrcnt)			continue;		/* Use the first valid address in bind addr list of		 * association as Address Parameter of ASCONF CHUNK.		 */		bp = &asoc->base.bind_addr;		p = bp->address_list.next;		laddr = list_entry(p, struct sctp_sockaddr_entry, list);		chunk = sctp_make_asconf_update_ip(asoc, &laddr->a, addrs,						   addrcnt, SCTP_PARAM_ADD_IP);		if (!chunk) {			retval = -ENOMEM;			goto out;		}		retval = sctp_send_asconf(asoc, chunk);		if (retval)			goto out;		/* Add the new addresses to the bind address list with		 * use_as_src set to 0.		 */		addr_buf = addrs;		for (i = 0; i < addrcnt; i++) {			addr = (union sctp_addr *)addr_buf;			af = sctp_get_af_specific(addr->v4.sin_family);			memcpy(&saveaddr, addr, af->sockaddr_len);			retval = sctp_add_bind_addr(bp, &saveaddr,						    SCTP_ADDR_NEW, GFP_ATOMIC);			addr_buf += af->sockaddr_len;		}	}out:	return retval;}/* Remove a list of addresses from bind addresses list.  Do not remove the * last address. * * Basically run through each address specified in the addrs/addrcnt * array/length pair, determine if it is IPv6 or IPv4 and call * sctp_del_bind() on it. * * If any of them fails, then the operation will be reversed and the * ones that were removed will be added back. * * At least one address has to be left; if only one address is * available, the operation will return -EBUSY. * * Only sctp_setsockopt_bindx() is supposed to call this function. */static int sctp_bindx_rem(struct sock *sk, struct sockaddr *addrs, int addrcnt){	struct sctp_sock *sp = sctp_sk(sk);	struct sctp_endpoint *ep = sp->ep;	int cnt;	struct sctp_bind_addr *bp = &ep->base.bind_addr;	int retval = 0;	void *addr_buf;	union sctp_addr *sa_addr;	struct sctp_af *af;	SCTP_DEBUG_PRINTK("sctp_bindx_rem (sk: %p, addrs: %p, addrcnt: %d)\n",			  sk, addrs, addrcnt);	addr_buf = addrs;	for (cnt = 0; cnt < addrcnt; cnt++) {		/* If the bind address list is empty or if there is only one		 * bind address, there is nothing more to be removed (we need		 * at least one address here).		 */		if (list_empty(&bp->address_list) ||		    (sctp_list_single_entry(&bp->address_list))) {			retval = -EBUSY;			goto err_bindx_rem;		}		sa_addr = (union sctp_addr *)addr_buf;		af = sctp_get_af_specific(sa_addr->sa.sa_family);		if (!af) {			retval = -EINVAL;			goto err_bindx_rem;		}		if (!af->addr_valid(sa_addr, sp, NULL)) {			retval = -EADDRNOTAVAIL;			goto err_bindx_rem;		}		if (sa_addr->v4.sin_port != htons(bp->port)) {			retval = -EINVAL;			goto err_bindx_rem;		}		/* FIXME - There is probably a need to check if sk->sk_saddr and		 * sk->sk_rcv_addr are currently set to one of the addresses to		 * be removed. This is something which needs to be looked into		 * when we are fixing the outstanding issues with multi-homing		 * socket routing and failover schemes. Refer to comments in		 * sctp_do_bind(). -daisy		 */		retval = sctp_del_bind_addr(bp, sa_addr);		addr_buf += af->sockaddr_len;err_bindx_rem:		if (retval < 0) {			/* Failed. Add the ones that has been removed back */			if (cnt > 0)				sctp_bindx_add(sk, addrs, cnt);			return retval;		}	}	return retval;}/* Send an ASCONF chunk with Delete IP address parameters to all the peers of * the associations that are part of the endpoint indicating that a list of * local addresses are removed from the endpoint. * * If any of the addresses is already in the bind address list of the * association, we do not send the chunk for that association.  But it will not * affect other associations. * * Only sctp_setsockopt_bindx() is supposed to call this function. */static int sctp_send_asconf_del_ip(struct sock		*sk,				   struct sockaddr	*addrs,				   int			addrcnt){	struct sctp_sock	*sp;	struct sctp_endpoint	*ep;	struct sctp_association	*asoc;	struct sctp_transport	*transport;	struct sctp_bind_addr	*bp;	struct sctp_chunk	*chunk;	union sctp_addr		*laddr;	void			*addr_buf;	struct sctp_af		*af;	struct list_head	*pos, *pos1;	struct sctp_sockaddr_entry *saddr;	int 			i;	int 			retval = 0;	if (!sctp_addip_enable)		return retval;	sp = sctp_sk(sk);	ep = sp->ep;	SCTP_DEBUG_PRINTK("%s: (sk: %p, addrs: %p, addrcnt: %d)\n",			  __FUNCTION__, sk, addrs, addrcnt);	list_for_each(pos, &ep->asocs) {		asoc = list_entry(pos, struct sctp_association, asocs);		if (!asoc->peer.asconf_capable)			continue;		if (asoc->peer.addip_disabled_mask & SCTP_PARAM_DEL_IP)			continue;		if (!sctp_state(asoc, ESTABLISHED))			continue;		/* Check if any address in the packed array of addresses is		 * not present in the bind address list of the association.		 * If so, do not send the asconf chunk to its peer, but		 * continue with other associations.		 */		addr_buf = addrs;		for (i = 0; i < addrcnt; i++) {			laddr = (union sctp_addr *)addr_buf;			af = sctp_get_af_specific(laddr->v4.sin_family);			if (!af) {				retval = -EINVAL;				goto out;			}			if (!sctp_assoc_lookup_laddr(asoc, laddr))				break;			addr_buf += af->sockaddr_len;		}		if (i < addrcnt)			continue;		/* Find one address in the association's bind address list		 * that is not in the packed array of addresses. This is to		 * make sure that we do not delete all the addresses in the		 * association.		 */		bp = &asoc->base.bind_addr;		laddr = sctp_find_unmatch_addr(bp, (union sctp_addr *)addrs,					       addrcnt, sp);		if (!laddr)			continue;		/* We do not need RCU protection throughout this loop		 * because this is done under a socket lock from the		 * setsockopt call.		 */		chunk = sctp_make_asconf_update_ip(asoc, laddr, addrs, addrcnt,						   SCTP_PARAM_DEL_IP);		if (!chunk) {			retval = -ENOMEM;			goto out;		}		/* Reset use_as_src flag for the addresses in the bind address		 * list that are to be deleted.		 */		addr_buf = addrs;		for (i = 0; i < addrcnt; i++) {			laddr = (union sctp_addr *)addr_buf;			af = sctp_get_af_specific(laddr->v4.sin_family);			list_for_each_entry(saddr, &bp->address_list, list) {				if (sctp_cmp_addr_exact(&saddr->a, laddr))					saddr->state = SCTP_ADDR_DEL;			}			addr_buf += af->sockaddr_len;		}		/* Update the route and saddr entries for all the transports		 * as some of the addresses in the bind address list are		 * about to be deleted and cannot be used as source addresses.		 */		list_for_each(pos1, &asoc->peer.transport_addr_list) {			transport = list_entry(pos1, struct sctp_transport,					       transports);			dst_release(transport->dst);			sctp_transport_route(transport, NULL,					     sctp_sk(asoc->base.sk));		}		retval = sctp_send_asconf(asoc, chunk);	}out:	return retval;}/* Helper for tunneling sctp_bindx() requests through sctp_setsockopt() * * API 8.1 * int sctp_bindx(int sd, struct sockaddr *addrs, int addrcnt, *                int flags); * * If sd is an IPv4 socket, the addresses passed must be IPv4 addresses. * If the sd is an IPv6 socket, the addresses passed can either be IPv4 * or IPv6 addresses. * * A single address may be specified as INADDR_ANY or IN6ADDR_ANY, see * Section 3.1.2 for this usage. * * addrs is a pointer to an array of one or more socket addresses. Each * address is contained in its appropriate structure (i.e. struct * sockaddr_in or struct sockaddr_in6) the family of the address type * must be used to distinguish the address length (note that this * representation is termed a "packed array" of addresses). The caller * specifies the number of addresses in the array with addrcnt. * * On success, sctp_bindx() returns 0. On failure, sctp_bindx() returns * -1, and sets errno to the appropriate error code. * * For SCTP, the port given in each socket address must be the same, or * sctp_bindx() will fail, setting errno to EINVAL. * * The flags parameter is formed from the bitwise OR of zero or more of * the following currently defined flags: * * SCTP_BINDX_ADD_ADDR * * SCTP_BINDX_REM_ADDR * * SCTP_BINDX_ADD_ADDR directs SCTP to add the given addresses to the * association, and SCTP_BINDX_REM_ADDR directs SCTP to remove the given * addresses from the association. The two flags are mutually exclusive; * if both are given, sctp_bindx() will fail with EINVAL. A caller may * not remove all addresses from an association; sctp_bindx() will * reject such an attempt with EINVAL. * * An application can use sctp_bindx(SCTP_BINDX_ADD_ADDR) to associate * additional addresses with an endpoint after calling bind().  Or use * sctp_bindx(SCTP_BINDX_REM_ADDR) to remove some addresses a listening * socket is associated with so that no new association accepted will be * associated with those addresses. If the endpoint supports dynamic * address a SCTP_BINDX_REM_ADDR or SCTP_BINDX_ADD_ADDR may cause a * endpoint to send the appropriate message to the peer to change the * peers address lists. * * Adding and removing addresses from a connected association is * optional functionality. Implementations that do not support this * functionality should return EOPNOTSUPP. * * Basically do nothing but copying the addresses from user to kernel * land and invoking either sctp_bindx_add() or sctp_bindx_rem() on the sk. * This is used for tunneling the sctp_bindx() request through sctp_setsockopt() * from userspace. * * We don't use copy_from_user() for optimization: we first do the * sanity checks (buffer size -fast- and access check-healthy * pointer); if all of those succeed, then we can alloc the memory * (expensive operation) needed to copy the data to kernel. Then we do * the copying without checking the user space area * (__copy_from_user()). * * On exit there is no need to do sockfd_put(), sys_setsockopt() does * it. * * sk        The sk of the socket * addrs     The pointer to the addresses in user land * addrssize Size of the addrs buffer

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -