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

📄 associola.c

📁 在linux环境下的流控制传输协议(sctp)的源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* Remove a transport from an association.  */void sctp_assoc_rm_peer(struct sctp_association *asoc,			struct sctp_transport *peer){	struct list_head	*pos;	struct sctp_transport	*transport;	SCTP_DEBUG_PRINTK_IPADDR("sctp_assoc_rm_peer:association %p addr: ",				 " port: %d\n",				 asoc,				 (&peer->ipaddr),				 ntohs(peer->ipaddr.v4.sin_port));	/* If we are to remove the current retran_path, update it	 * to the next peer before removing this peer from the list.	 */	if (asoc->peer.retran_path == peer)		sctp_assoc_update_retran_path(asoc);	/* Remove this peer from the list. */	list_del(&peer->transports);	/* Get the first transport of asoc. */	pos = asoc->peer.transport_addr_list.next;	transport = list_entry(pos, struct sctp_transport, transports);	/* Update any entries that match the peer to be deleted. */	if (asoc->peer.primary_path == peer)		sctp_assoc_set_primary(asoc, transport);	if (asoc->peer.active_path == peer)		asoc->peer.active_path = transport;	if (asoc->peer.last_data_from == peer)		asoc->peer.last_data_from = transport;	/* If we remove the transport an INIT was last sent to, set it to	 * NULL. Combined with the update of the retran path above, this	 * will cause the next INIT to be sent to the next available	 * transport, maintaining the cycle.	 */	if (asoc->init_last_sent_to == peer)		asoc->init_last_sent_to = NULL;	asoc->peer.transport_count--;	sctp_transport_free(peer);}/* Add a transport address to an association.  */struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc,					   const union sctp_addr *addr,					   const gfp_t gfp,					   const int peer_state){	struct sctp_transport *peer;	struct sctp_sock *sp;	unsigned short port;	sp = sctp_sk(asoc->base.sk);	/* AF_INET and AF_INET6 share common port field. */	port = ntohs(addr->v4.sin_port);	SCTP_DEBUG_PRINTK_IPADDR("sctp_assoc_add_peer:association %p addr: ",				 " port: %d state:%d\n",				 asoc,				 addr,				 port,				 peer_state);	/* Set the port if it has not been set yet.  */	if (0 == asoc->peer.port)		asoc->peer.port = port;	/* Check to see if this is a duplicate. */	peer = sctp_assoc_lookup_paddr(asoc, addr);	if (peer) {		if (peer->state == SCTP_UNKNOWN) {			if (peer_state == SCTP_ACTIVE)				peer->state = SCTP_ACTIVE;			if (peer_state == SCTP_UNCONFIRMED)				peer->state = SCTP_UNCONFIRMED;		}		return peer;	}	peer = sctp_transport_new(addr, gfp);	if (!peer)		return NULL;	sctp_transport_set_owner(peer, asoc);	/* Initialize the peer's heartbeat interval based on the	 * association configured value.	 */	peer->hbinterval = asoc->hbinterval;	/* Set the path max_retrans.  */	peer->pathmaxrxt = asoc->pathmaxrxt;	/* Initialize the peer's SACK delay timeout based on the	 * association configured value.	 */	peer->sackdelay = asoc->sackdelay;	/* Enable/disable heartbeat, SACK delay, and path MTU discovery	 * based on association setting.	 */	peer->param_flags = asoc->param_flags;	/* Initialize the pmtu of the transport. */	if (peer->param_flags & SPP_PMTUD_ENABLE)		sctp_transport_pmtu(peer);	else if (asoc->pathmtu)		peer->pathmtu = asoc->pathmtu;	else		peer->pathmtu = SCTP_DEFAULT_MAXSEGMENT;	/* If this is the first transport addr on this association,	 * initialize the association PMTU to the peer's PMTU.	 * If not and the current association PMTU is higher than the new	 * peer's PMTU, reset the association PMTU to the new peer's PMTU.	 */	if (asoc->pathmtu)		asoc->pathmtu = min_t(int, peer->pathmtu, asoc->pathmtu);	else		asoc->pathmtu = peer->pathmtu;	SCTP_DEBUG_PRINTK("sctp_assoc_add_peer:association %p PMTU set to "			  "%d\n", asoc, asoc->pathmtu);	asoc->frag_point = sctp_frag_point(sp, asoc->pathmtu);	/* The asoc->peer.port might not be meaningful yet, but	 * initialize the packet structure anyway.	 */	sctp_packet_init(&peer->packet, peer, asoc->base.bind_addr.port,			 asoc->peer.port);	/* 7.2.1 Slow-Start	 *	 * o The initial cwnd before DATA transmission or after a sufficiently	 *   long idle period MUST be set to	 *      min(4*MTU, max(2*MTU, 4380 bytes))	 *	 * o The initial value of ssthresh MAY be arbitrarily high	 *   (for example, implementations MAY use the size of the	 *   receiver advertised window).	 */	peer->cwnd = min(4*asoc->pathmtu, max_t(__u32, 2*asoc->pathmtu, 4380));	/* At this point, we may not have the receiver's advertised window,	 * so initialize ssthresh to the default value and it will be set	 * later when we process the INIT.	 */	peer->ssthresh = SCTP_DEFAULT_MAXWINDOW;	peer->partial_bytes_acked = 0;	peer->flight_size = 0;	/* Set the transport's RTO.initial value */	peer->rto = asoc->rto_initial;	/* Set the peer's active state. */	peer->state = peer_state;	/* Attach the remote transport to our asoc.  */	list_add_tail(&peer->transports, &asoc->peer.transport_addr_list);	asoc->peer.transport_count++;	/* If we do not yet have a primary path, set one.  */	if (!asoc->peer.primary_path) {		sctp_assoc_set_primary(asoc, peer);		asoc->peer.retran_path = peer;	}	if (asoc->peer.active_path == asoc->peer.retran_path) {		asoc->peer.retran_path = peer;	}	return peer;}/* Delete a transport address from an association.  */void sctp_assoc_del_peer(struct sctp_association *asoc,			 const union sctp_addr *addr){	struct list_head	*pos;	struct list_head	*temp;	struct sctp_transport	*transport;	list_for_each_safe(pos, temp, &asoc->peer.transport_addr_list) {		transport = list_entry(pos, struct sctp_transport, transports);		if (sctp_cmp_addr_exact(addr, &transport->ipaddr)) {			/* Do book keeping for removing the peer and free it. */			sctp_assoc_rm_peer(asoc, transport);			break;		}	}}/* Lookup a transport by address. */struct sctp_transport *sctp_assoc_lookup_paddr(					const struct sctp_association *asoc,					const union sctp_addr *address){	struct sctp_transport *t;	struct list_head *pos;	/* Cycle through all transports searching for a peer address. */	list_for_each(pos, &asoc->peer.transport_addr_list) {		t = list_entry(pos, struct sctp_transport, transports);		if (sctp_cmp_addr_exact(address, &t->ipaddr))			return t;	}	return NULL;}/* Remove all transports except a give one */void sctp_assoc_del_nonprimary_peers(struct sctp_association *asoc,				     struct sctp_transport *primary){	struct sctp_transport	*temp;	struct sctp_transport	*t;	list_for_each_entry_safe(t, temp, &asoc->peer.transport_addr_list,				 transports) {		/* if the current transport is not the primary one, delete it */		if (t != primary)			sctp_assoc_rm_peer(asoc, t);	}	return;}/* Engage in transport control operations. * Mark the transport up or down and send a notification to the user. * Select and update the new active and retran paths. */void sctp_assoc_control_transport(struct sctp_association *asoc,				  struct sctp_transport *transport,				  sctp_transport_cmd_t command,				  sctp_sn_error_t error){	struct sctp_transport *t = NULL;	struct sctp_transport *first;	struct sctp_transport *second;	struct sctp_ulpevent *event;	struct sockaddr_storage addr;	struct list_head *pos;	int spc_state = 0;	/* Record the transition on the transport.  */	switch (command) {	case SCTP_TRANSPORT_UP:		/* If we are moving from UNCONFIRMED state due		 * to heartbeat success, report the SCTP_ADDR_CONFIRMED		 * state to the user, otherwise report SCTP_ADDR_AVAILABLE.		 */		if (SCTP_UNCONFIRMED == transport->state &&		    SCTP_HEARTBEAT_SUCCESS == error)			spc_state = SCTP_ADDR_CONFIRMED;		else			spc_state = SCTP_ADDR_AVAILABLE;		transport->state = SCTP_ACTIVE;		break;	case SCTP_TRANSPORT_DOWN:		/* if the transort was never confirmed, do not transition it		 * to inactive state.		 */		if (transport->state != SCTP_UNCONFIRMED)			transport->state = SCTP_INACTIVE;		spc_state = SCTP_ADDR_UNREACHABLE;		break;	default:		return;	}	/* Generate and send a SCTP_PEER_ADDR_CHANGE notification to the	 * user.	 */	memset(&addr, 0, sizeof(struct sockaddr_storage));	memcpy(&addr, &transport->ipaddr, transport->af_specific->sockaddr_len);	event = sctp_ulpevent_make_peer_addr_change(asoc, &addr,				0, spc_state, error, GFP_ATOMIC);	if (event)		sctp_ulpq_tail_event(&asoc->ulpq, event);	/* Select new active and retran paths. */	/* Look for the two most recently used active transports.	 *	 * This code produces the wrong ordering whenever jiffies	 * rolls over, but we still get usable transports, so we don't	 * worry about it.	 */	first = NULL; second = NULL;	list_for_each(pos, &asoc->peer.transport_addr_list) {		t = list_entry(pos, struct sctp_transport, transports);		if ((t->state == SCTP_INACTIVE) ||		    (t->state == SCTP_UNCONFIRMED))			continue;		if (!first || t->last_time_heard > first->last_time_heard) {			second = first;			first = t;		}		if (!second || t->last_time_heard > second->last_time_heard)			second = t;	}	/* RFC 2960 6.4 Multi-Homed SCTP Endpoints	 *	 * By default, an endpoint should always transmit to the	 * primary path, unless the SCTP user explicitly specifies the	 * destination transport address (and possibly source	 * transport address) to use.	 *	 * [If the primary is active but not most recent, bump the most	 * recently used transport.]	 */	if (((asoc->peer.primary_path->state == SCTP_ACTIVE) ||	     (asoc->peer.primary_path->state == SCTP_UNKNOWN)) &&	    first != asoc->peer.primary_path) {		second = first;		first = asoc->peer.primary_path;	}	/* If we failed to find a usable transport, just camp on the	 * primary, even if it is inactive.	 */	if (!first) {		first = asoc->peer.primary_path;		second = asoc->peer.primary_path;	}	/* Set the active and retran transports.  */	asoc->peer.active_path = first;	asoc->peer.retran_path = second;}/* Hold a reference to an association. */void sctp_association_hold(struct sctp_association *asoc){	atomic_inc(&asoc->base.refcnt);}/* Release a reference to an association and cleanup * if there are no more references. */void sctp_association_put(struct sctp_association *asoc){	if (atomic_dec_and_test(&asoc->base.refcnt))		sctp_association_destroy(asoc);}/* Allocate the next TSN, Transmission Sequence Number, for the given * association. */__u32 sctp_association_get_next_tsn(struct sctp_association *asoc){	/* From Section 1.6 Serial Number Arithmetic:	 * Transmission Sequence Numbers wrap around when they reach	 * 2**32 - 1.  That is, the next TSN a DATA chunk MUST use	 * after transmitting TSN = 2*32 - 1 is TSN = 0.	 */	__u32 retval = asoc->next_tsn;	asoc->next_tsn++;	asoc->unack_data++;	return retval;}/* Compare two addresses to see if they match.  Wildcard addresses * only match themselves. */int sctp_cmp_addr_exact(const union sctp_addr *ss1,			const union sctp_addr *ss2){	struct sctp_af *af;	af = sctp_get_af_specific(ss1->sa.sa_family);	if (unlikely(!af))		return 0;	return af->cmp_addr(ss1, ss2);}/* Return an ecne chunk to get prepended to a packet. * Note:  We are sly and return a shared, prealloced chunk.  FIXME: * No we don't, but we could/should. */struct sctp_chunk *sctp_get_ecne_prepend(struct sctp_association *asoc){	struct sctp_chunk *chunk;	/* Send ECNE if needed.	 * Not being able to allocate a chunk here is not deadly.	 */	if (asoc->need_ecne)		chunk = sctp_make_ecne(asoc, asoc->last_ecne_tsn);	else		chunk = NULL;	return chunk;}/* * Find which transport this TSN was sent on. */struct sctp_transport *sctp_assoc_lookup_tsn(struct sctp_association *asoc,					     __u32 tsn){	struct sctp_transport *active;	struct sctp_transport *match;	struct list_head *entry, *pos;	struct sctp_transport *transport;	struct sctp_chunk *chunk;	__be32 key = htonl(tsn);	match = NULL;	/*	 * FIXME: In general, find a more efficient data structure for	 * searching.	 */	/*	 * The general strategy is to search each transport's transmitted	 * list.   Return which transport this TSN lives on.	 *	 * Let's be hopeful and check the active_path first.	 * Another optimization would be to know if there is only one	 * outbound path and not have to look for the TSN at all.	 *	 */	active = asoc->peer.active_path;	list_for_each(entry, &active->transmitted) {		chunk = list_entry(entry, struct sctp_chunk, transmitted_list);		if (key == chunk->subh.data_hdr->tsn) {			match = active;			goto out;		}	}	/* If not found, go search all the other transports. */	list_for_each(pos, &asoc->peer.transport_addr_list) {		transport = list_entry(pos, struct sctp_transport, transports);		if (transport == active)			break;		list_for_each(entry, &transport->transmitted) {			chunk = list_entry(entry, struct sctp_chunk,					   transmitted_list);			if (key == chunk->subh.data_hdr->tsn) {				match = transport;				goto out;			}		}	}out:	return match;}/* Is this the association we are looking for? */struct sctp_transport *sctp_assoc_is_match(struct sctp_association *asoc,					   const union sctp_addr *laddr,					   const union sctp_addr *paddr){	struct sctp_transport *transport;	if ((htons(asoc->base.bind_addr.port) == laddr->v4.sin_port) &&	    (htons(asoc->peer.port) == paddr->v4.sin_port)) {		transport = sctp_assoc_lookup_paddr(asoc, paddr);		if (!transport)			goto out;		if (sctp_bind_addr_match(&asoc->base.bind_addr, laddr,					 sctp_sk(asoc->base.sk)))			goto out;	}	transport = NULL;out:	return transport;}/* Do delayed input processing.  This is scheduled by sctp_rcv(). */static void sctp_assoc_bh_rcv(struct work_struct *work){	struct sctp_association *asoc =		container_of(work, struct sctp_association,			     base.inqueue.immediate);	struct sctp_endpoint *ep;	struct sctp_chunk *chunk;	struct sock *sk;	struct sctp_inq *inqueue;	int state;	sctp_subtype_t subtype;	int error = 0;	/* The association should be held so we should be safe. */	ep = asoc->ep;	sk = asoc->base.sk;	inqueue = &asoc->base.inqueue;

⌨️ 快捷键说明

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