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

📄 associola.c

📁 在linux环境下的流控制传输协议(sctp)的源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
	sctp_association_hold(asoc);	while (NULL != (chunk = sctp_inq_pop(inqueue))) {		state = asoc->state;		subtype = SCTP_ST_CHUNK(chunk->chunk_hdr->type);		/* SCTP-AUTH, Section 6.3:		 *    The receiver has a list of chunk types which it expects		 *    to be received only after an AUTH-chunk.  This list has		 *    been sent to the peer during the association setup.  It		 *    MUST silently discard these chunks if they are not placed		 *    after an AUTH chunk in the packet.		 */		if (sctp_auth_recv_cid(subtype.chunk, asoc) && !chunk->auth)			continue;		/* Remember where the last DATA chunk came from so we		 * know where to send the SACK.		 */		if (sctp_chunk_is_data(chunk))			asoc->peer.last_data_from = chunk->transport;		else			SCTP_INC_STATS(SCTP_MIB_INCTRLCHUNKS);		if (chunk->transport)			chunk->transport->last_time_heard = jiffies;		/* Run through the state machine. */		error = sctp_do_sm(SCTP_EVENT_T_CHUNK, subtype,				   state, ep, asoc, chunk, GFP_ATOMIC);		/* Check to see if the association is freed in response to		 * the incoming chunk.  If so, get out of the while loop.		 */		if (asoc->base.dead)			break;		/* If there is an error on chunk, discard this packet. */		if (error && chunk)			chunk->pdiscard = 1;	}	sctp_association_put(asoc);}/* This routine moves an association from its old sk to a new sk.  */void sctp_assoc_migrate(struct sctp_association *assoc, struct sock *newsk){	struct sctp_sock *newsp = sctp_sk(newsk);	struct sock *oldsk = assoc->base.sk;	/* Delete the association from the old endpoint's list of	 * associations.	 */	list_del_init(&assoc->asocs);	/* Decrement the backlog value for a TCP-style socket. */	if (sctp_style(oldsk, TCP))		oldsk->sk_ack_backlog--;	/* Release references to the old endpoint and the sock.  */	sctp_endpoint_put(assoc->ep);	sock_put(assoc->base.sk);	/* Get a reference to the new endpoint.  */	assoc->ep = newsp->ep;	sctp_endpoint_hold(assoc->ep);	/* Get a reference to the new sock.  */	assoc->base.sk = newsk;	sock_hold(assoc->base.sk);	/* Add the association to the new endpoint's list of associations.  */	sctp_endpoint_add_asoc(newsp->ep, assoc);}/* Update an association (possibly from unexpected COOKIE-ECHO processing).  */void sctp_assoc_update(struct sctp_association *asoc,		       struct sctp_association *new){	struct sctp_transport *trans;	struct list_head *pos, *temp;	/* Copy in new parameters of peer. */	asoc->c = new->c;	asoc->peer.rwnd = new->peer.rwnd;	asoc->peer.sack_needed = new->peer.sack_needed;	asoc->peer.i = new->peer.i;	sctp_tsnmap_init(&asoc->peer.tsn_map, SCTP_TSN_MAP_SIZE,			 asoc->peer.i.initial_tsn);	/* Remove any peer addresses not present in the new association. */	list_for_each_safe(pos, temp, &asoc->peer.transport_addr_list) {		trans = list_entry(pos, struct sctp_transport, transports);		if (!sctp_assoc_lookup_paddr(new, &trans->ipaddr))			sctp_assoc_del_peer(asoc, &trans->ipaddr);		if (asoc->state >= SCTP_STATE_ESTABLISHED)			sctp_transport_reset(trans);	}	/* If the case is A (association restart), use	 * initial_tsn as next_tsn. If the case is B, use	 * current next_tsn in case data sent to peer	 * has been discarded and needs retransmission.	 */	if (asoc->state >= SCTP_STATE_ESTABLISHED) {		asoc->next_tsn = new->next_tsn;		asoc->ctsn_ack_point = new->ctsn_ack_point;		asoc->adv_peer_ack_point = new->adv_peer_ack_point;		/* Reinitialize SSN for both local streams		 * and peer's streams.		 */		sctp_ssnmap_clear(asoc->ssnmap);		/* Flush the ULP reassembly and ordered queue.		 * Any data there will now be stale and will		 * cause problems.		 */		sctp_ulpq_flush(&asoc->ulpq);		/* reset the overall association error count so		 * that the restarted association doesn't get torn		 * down on the next retransmission timer.		 */		asoc->overall_error_count = 0;	} else {		/* Add any peer addresses from the new association. */		list_for_each(pos, &new->peer.transport_addr_list) {			trans = list_entry(pos, struct sctp_transport,					   transports);			if (!sctp_assoc_lookup_paddr(asoc, &trans->ipaddr))				sctp_assoc_add_peer(asoc, &trans->ipaddr,						    GFP_ATOMIC, trans->state);		}		asoc->ctsn_ack_point = asoc->next_tsn - 1;		asoc->adv_peer_ack_point = asoc->ctsn_ack_point;		if (!asoc->ssnmap) {			/* Move the ssnmap. */			asoc->ssnmap = new->ssnmap;			new->ssnmap = NULL;		}		if (!asoc->assoc_id) {			/* get a new association id since we don't have one			 * yet.			 */			sctp_assoc_set_id(asoc, GFP_ATOMIC);		}	}	/* SCTP-AUTH: Save the peer parameters from the new assocaitions	 * and also move the association shared keys over	 */	kfree(asoc->peer.peer_random);	asoc->peer.peer_random = new->peer.peer_random;	new->peer.peer_random = NULL;	kfree(asoc->peer.peer_chunks);	asoc->peer.peer_chunks = new->peer.peer_chunks;	new->peer.peer_chunks = NULL;	kfree(asoc->peer.peer_hmacs);	asoc->peer.peer_hmacs = new->peer.peer_hmacs;	new->peer.peer_hmacs = NULL;	sctp_auth_key_put(asoc->asoc_shared_key);	sctp_auth_asoc_init_active_key(asoc, GFP_ATOMIC);}/* Update the retran path for sending a retransmitted packet. * Round-robin through the active transports, else round-robin * through the inactive transports as this is the next best thing * we can try. */void sctp_assoc_update_retran_path(struct sctp_association *asoc){	struct sctp_transport *t, *next;	struct list_head *head = &asoc->peer.transport_addr_list;	struct list_head *pos;	/* Find the next transport in a round-robin fashion. */	t = asoc->peer.retran_path;	pos = &t->transports;	next = NULL;	while (1) {		/* Skip the head. */		if (pos->next == head)			pos = head->next;		else			pos = pos->next;		t = list_entry(pos, struct sctp_transport, transports);		/* Try to find an active transport. */		if ((t->state == SCTP_ACTIVE) ||		    (t->state == SCTP_UNKNOWN)) {			break;		} else {			/* Keep track of the next transport in case			 * we don't find any active transport.			 */			if (!next)				next = t;		}		/* We have exhausted the list, but didn't find any		 * other active transports.  If so, use the next		 * transport.		 */		if (t == asoc->peer.retran_path) {			t = next;			break;		}	}	asoc->peer.retran_path = t;	SCTP_DEBUG_PRINTK_IPADDR("sctp_assoc_update_retran_path:association"				 " %p addr: ",				 " port: %d\n",				 asoc,				 (&t->ipaddr),				 ntohs(t->ipaddr.v4.sin_port));}/* Choose the transport for sending a INIT packet.  */struct sctp_transport *sctp_assoc_choose_init_transport(	struct sctp_association *asoc){	struct sctp_transport *t;	/* Use the retran path. If the last INIT was sent over the	 * retran path, update the retran path and use it.	 */	if (!asoc->init_last_sent_to) {		t = asoc->peer.active_path;	} else {		if (asoc->init_last_sent_to == asoc->peer.retran_path)			sctp_assoc_update_retran_path(asoc);		t = asoc->peer.retran_path;	}	SCTP_DEBUG_PRINTK_IPADDR("sctp_assoc_update_retran_path:association"				 " %p addr: ",				 " port: %d\n",				 asoc,				 (&t->ipaddr),				 ntohs(t->ipaddr.v4.sin_port));	return t;}/* Choose the transport for sending a SHUTDOWN packet.  */struct sctp_transport *sctp_assoc_choose_shutdown_transport(	struct sctp_association *asoc){	/* If this is the first time SHUTDOWN is sent, use the active path,	 * else use the retran path. If the last SHUTDOWN was sent over the	 * retran path, update the retran path and use it.	 */	if (!asoc->shutdown_last_sent_to)		return asoc->peer.active_path;	else {		if (asoc->shutdown_last_sent_to == asoc->peer.retran_path)			sctp_assoc_update_retran_path(asoc);		return asoc->peer.retran_path;	}}/* Update the association's pmtu and frag_point by going through all the * transports. This routine is called when a transport's PMTU has changed. */void sctp_assoc_sync_pmtu(struct sctp_association *asoc){	struct sctp_transport *t;	struct list_head *pos;	__u32 pmtu = 0;	if (!asoc)		return;	/* Get the lowest pmtu of all the transports. */	list_for_each(pos, &asoc->peer.transport_addr_list) {		t = list_entry(pos, struct sctp_transport, transports);		if (t->pmtu_pending && t->dst) {			sctp_transport_update_pmtu(t, dst_mtu(t->dst));			t->pmtu_pending = 0;		}		if (!pmtu || (t->pathmtu < pmtu))			pmtu = t->pathmtu;	}	if (pmtu) {		struct sctp_sock *sp = sctp_sk(asoc->base.sk);		asoc->pathmtu = pmtu;		asoc->frag_point = sctp_frag_point(sp, pmtu);	}	SCTP_DEBUG_PRINTK("%s: asoc:%p, pmtu:%d, frag_point:%d\n",			  __FUNCTION__, asoc, asoc->pathmtu, asoc->frag_point);}/* Should we send a SACK to update our peer? */static inline int sctp_peer_needs_update(struct sctp_association *asoc){	switch (asoc->state) {	case SCTP_STATE_ESTABLISHED:	case SCTP_STATE_SHUTDOWN_PENDING:	case SCTP_STATE_SHUTDOWN_RECEIVED:	case SCTP_STATE_SHUTDOWN_SENT:		if ((asoc->rwnd > asoc->a_rwnd) &&		    ((asoc->rwnd - asoc->a_rwnd) >=		     min_t(__u32, (asoc->base.sk->sk_rcvbuf >> 1), asoc->pathmtu)))			return 1;		break;	default:		break;	}	return 0;}/* Increase asoc's rwnd by len and send any window update SACK if needed. */void sctp_assoc_rwnd_increase(struct sctp_association *asoc, unsigned len){	struct sctp_chunk *sack;	struct timer_list *timer;	if (asoc->rwnd_over) {		if (asoc->rwnd_over >= len) {			asoc->rwnd_over -= len;		} else {			asoc->rwnd += (len - asoc->rwnd_over);			asoc->rwnd_over = 0;		}	} else {		asoc->rwnd += len;	}	SCTP_DEBUG_PRINTK("%s: asoc %p rwnd increased by %d to (%u, %u) "			  "- %u\n", __FUNCTION__, asoc, len, asoc->rwnd,			  asoc->rwnd_over, asoc->a_rwnd);	/* Send a window update SACK if the rwnd has increased by at least the	 * minimum of the association's PMTU and half of the receive buffer.	 * The algorithm used is similar to the one described in	 * Section 4.2.3.3 of RFC 1122.	 */	if (sctp_peer_needs_update(asoc)) {		asoc->a_rwnd = asoc->rwnd;		SCTP_DEBUG_PRINTK("%s: Sending window update SACK- asoc: %p "				  "rwnd: %u a_rwnd: %u\n", __FUNCTION__,				  asoc, asoc->rwnd, asoc->a_rwnd);		sack = sctp_make_sack(asoc);		if (!sack)			return;		asoc->peer.sack_needed = 0;		sctp_outq_tail(&asoc->outqueue, sack);		/* Stop the SACK timer.  */		timer = &asoc->timers[SCTP_EVENT_TIMEOUT_SACK];		if (timer_pending(timer) && del_timer(timer))			sctp_association_put(asoc);	}}/* Decrease asoc's rwnd by len. */void sctp_assoc_rwnd_decrease(struct sctp_association *asoc, unsigned len){	SCTP_ASSERT(asoc->rwnd, "rwnd zero", return);	SCTP_ASSERT(!asoc->rwnd_over, "rwnd_over not zero", return);	if (asoc->rwnd >= len) {		asoc->rwnd -= len;	} else {		asoc->rwnd_over = len - asoc->rwnd;		asoc->rwnd = 0;	}	SCTP_DEBUG_PRINTK("%s: asoc %p rwnd decreased by %d to (%u, %u)\n",			  __FUNCTION__, asoc, len, asoc->rwnd,			  asoc->rwnd_over);}/* Build the bind address list for the association based on info from the * local endpoint and the remote peer. */int sctp_assoc_set_bind_addr_from_ep(struct sctp_association *asoc,				     gfp_t gfp){	sctp_scope_t scope;	int flags;	/* Use scoping rules to determine the subset of addresses from	 * the endpoint.	 */	scope = sctp_scope(&asoc->peer.active_path->ipaddr);	flags = (PF_INET6 == asoc->base.sk->sk_family) ? SCTP_ADDR6_ALLOWED : 0;	if (asoc->peer.ipv4_address)		flags |= SCTP_ADDR4_PEERSUPP;	if (asoc->peer.ipv6_address)		flags |= SCTP_ADDR6_PEERSUPP;	return sctp_bind_addr_copy(&asoc->base.bind_addr,				   &asoc->ep->base.bind_addr,				   scope, gfp, flags);}/* Build the association's bind address list from the cookie.  */int sctp_assoc_set_bind_addr_from_cookie(struct sctp_association *asoc,					 struct sctp_cookie *cookie,					 gfp_t gfp){	int var_size2 = ntohs(cookie->peer_init->chunk_hdr.length);	int var_size3 = cookie->raw_addr_list_len;	__u8 *raw = (__u8 *)cookie->peer_init + var_size2;	return sctp_raw_to_bind_addrs(&asoc->base.bind_addr, raw, var_size3,				      asoc->ep->base.bind_addr.port, gfp);}/* Lookup laddr in the bind address list of an association. */int sctp_assoc_lookup_laddr(struct sctp_association *asoc,			    const union sctp_addr *laddr){	int found = 0;	if ((asoc->base.bind_addr.port == ntohs(laddr->v4.sin_port)) &&	    sctp_bind_addr_match(&asoc->base.bind_addr, laddr,				 sctp_sk(asoc->base.sk)))		found = 1;	return found;}/* Set an association id for a given association */int sctp_assoc_set_id(struct sctp_association *asoc, gfp_t gfp){	int assoc_id;	int error = 0;retry:	if (unlikely(!idr_pre_get(&sctp_assocs_id, gfp)))		return -ENOMEM;	spin_lock_bh(&sctp_assocs_id_lock);	error = idr_get_new_above(&sctp_assocs_id, (void *)asoc,				    1, &assoc_id);	spin_unlock_bh(&sctp_assocs_id_lock);	if (error == -EAGAIN)		goto retry;	else if (error)		return error;	asoc->assoc_id = (sctp_assoc_t) assoc_id;	return error;}/* Free asconf_ack cache */static void sctp_assoc_free_asconf_acks(struct sctp_association *asoc){	struct sctp_chunk *ack;	struct sctp_chunk *tmp;	list_for_each_entry_safe(ack, tmp, &asoc->asconf_ack_list,				transmitted_list) {		list_del_init(&ack->transmitted_list);		sctp_chunk_free(ack);	}}/* Clean up the ASCONF_ACK queue */void sctp_assoc_clean_asconf_ack_cache(const struct sctp_association *asoc){	struct sctp_chunk *ack;	struct sctp_chunk *tmp;	/* We can remove all the entries from the queue upto	 * the "Peer-Sequence-Number".	 */	list_for_each_entry_safe(ack, tmp, &asoc->asconf_ack_list,				transmitted_list) {		if (ack->subh.addip_hdr->serial ==				htonl(asoc->peer.addip_serial))			break;		list_del_init(&ack->transmitted_list);		sctp_chunk_free(ack);	}}/* Find the ASCONF_ACK whose serial number matches ASCONF */struct sctp_chunk *sctp_assoc_lookup_asconf_ack(					const struct sctp_association *asoc,					__be32 serial){	struct sctp_chunk *ack;	/* Walk through the list of cached ASCONF-ACKs and find the	 * ack chunk whose serial number matches that of the request.	 */	list_for_each_entry(ack, &asoc->asconf_ack_list, transmitted_list) {		if (ack->subh.addip_hdr->serial == serial) {			sctp_chunk_hold(ack);			return ack;		}	}	return NULL;}

⌨️ 快捷键说明

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