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

📄 socket.c

📁 一种在UDP协议中实现了拥赛控制和重传机制的协议
💻 C
📖 第 1 页 / 共 5 页
字号:
	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;	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 address in bind addr list of association as		 * Address Parameter of ASCONF CHUNK.		 */		sctp_read_lock(&asoc->base.addr_lock);		bp = &asoc->base.bind_addr;		p = bp->address_list.next;		laddr = list_entry(p, struct sctp_sockaddr_entry, list);		sctp_read_unlock(&asoc->base.addr_lock);		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);		/* FIXME: After sending the add address ASCONF chunk, we		 * cannot append the address to the association's binding		 * address list, because the new address may be used as the		 * source of a message sent to the peer before the ASCONF		 * chunk is received by the peer.  So we should wait until		 * ASCONF_ACK is received.		 */	}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. */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;	union sctp_addr saveaddr;	void *addr_buf;	struct sockaddr *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;		}		/* The list may contain either IPv4 or IPv6 address;		 * determine the address length to copy the address to		 * saveaddr. 		 */		sa_addr = (struct sockaddr *)addr_buf;		af = sctp_get_af_specific(sa_addr->sa_family);		if (!af) {			retval = -EINVAL;			goto err_bindx_rem;		}		memcpy(&saveaddr, sa_addr, af->sockaddr_len); 		saveaddr.v4.sin_port = ntohs(saveaddr.v4.sin_port);		if (saveaddr.v4.sin_port != 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		 */		sctp_local_bh_disable();		sctp_write_lock(&ep->base.addr_lock);		retval = sctp_del_bind_addr(bp, &saveaddr);		sctp_write_unlock(&ep->base.addr_lock);		sctp_local_bh_enable();		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_bind_addr	*bp;	struct sctp_chunk	*chunk;	union sctp_addr		*laddr;	void			*addr_buf;	struct sctp_af		*af;	struct list_head	*pos;	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.		 */		sctp_read_lock(&asoc->base.addr_lock);		bp = &asoc->base.bind_addr;		laddr = sctp_find_unmatch_addr(bp, (union sctp_addr *)addrs,					       addrcnt, sp);		sctp_read_unlock(&asoc->base.addr_lock);		if (!laddr)			continue;		chunk = sctp_make_asconf_update_ip(asoc, laddr, addrs, addrcnt,						   SCTP_PARAM_DEL_IP);		if (!chunk) {			retval = -ENOMEM;			goto out;		}		retval = sctp_send_asconf(asoc, chunk);		/* FIXME: After sending the delete address ASCONF chunk, we		 * cannot remove the addresses from the association's bind		 * address list, because there maybe some packet send to		 * the delete addresses, so we should wait until ASCONF_ACK		 * packet is received.		 */	}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 distengish 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 * op        Operation to perform (add or remove, see the flags of *           sctp_bindx) * * Returns 0 if ok, <0 errno code on error. */SCTP_STATIC int sctp_setsockopt_bindx(struct sock* sk,				      struct sockaddr __user *addrs,				      int addrs_size, int op){	struct sockaddr *kaddrs;	int err;	int addrcnt = 0;	int walk_size = 0;	struct sockaddr *sa_addr;	void *addr_buf;	struct sctp_af *af;	SCTP_DEBUG_PRINTK("sctp_setsocktopt_bindx: sk %p addrs %p"			  " addrs_size %d opt %d\n", sk, addrs, addrs_size, op);	if (unlikely(addrs_size <= 0))		return -EINVAL;	/* Check the user passed a healthy pointer.  */	if (unlikely(!access_ok(VERIFY_READ, addrs, addrs_size)))		return -EFAULT;	/* Alloc space for the address array in kernel memory.  */	kaddrs = (struct sockaddr *)kmalloc(addrs_size, GFP_KERNEL);	if (unlikely(!kaddrs))		return -ENOMEM;	if (__copy_from_user(kaddrs, addrs, addrs_size)) {		kfree(kaddrs);		return -EFAULT;	}	/* Walk through the addrs buffer and count the number of addresses. */ 	addr_buf = kaddrs;	while (walk_size < addrs_size) {		sa_addr = (struct sockaddr *)addr_buf;		af = sctp_get_af_specific(sa_addr->sa_family);		/* If the address family is not supported or if this address		 * causes the address buffer to overflow return EINVAL.		 */ 		if (!af || (walk_size + af->sockaddr_len) > addrs_size) {			kfree(kaddrs);			return -EINVAL;		}		addrcnt++;		addr_buf += af->sockaddr_len;		walk_size += af->sockaddr_len;

⌨️ 快捷键说明

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