📄 associola.c
字号:
* primary destination, the sender MUST do the following: * * 1) If CHANGEOVER_ACTIVE is set, then there was a switch * to this destination address earlier. The sender MUST set * CYCLING_CHANGEOVER to indicate that this switch is a * double switch to the same destination address. */ if (transport->cacc.changeover_active) transport->cacc.cycling_changeover = 1; /* 2) The sender MUST set CHANGEOVER_ACTIVE to indicate that * a changeover has occurred. */ transport->cacc.changeover_active = 1; /* 3) The sender MUST store the next TSN to be sent in * next_tsn_at_change. */ transport->cacc.next_tsn_at_change = asoc->next_tsn;}/* Add a transport address to an association. */struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc, const union sctp_addr *addr, int gfp){ struct sctp_transport *peer; struct sctp_opt *sp; unsigned short port; sp = sctp_sk(asoc->base.sk); /* AF_INET and AF_INET6 share common port field. */ port = addr->v4.sin_port; /* 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) return peer; peer = sctp_transport_new(addr, gfp); if (!peer) return NULL; sctp_transport_set_owner(peer, asoc); /* Initialize the pmtu of the transport. */ sctp_transport_pmtu(peer); /* 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->pmtu) asoc->pmtu = min_t(int, peer->pmtu, asoc->pmtu); else asoc->pmtu = peer->pmtu; SCTP_DEBUG_PRINTK("sctp_assoc_add_peer:association %p PMTU set to " "%d\n", asoc, asoc->pmtu); asoc->frag_point = sctp_frag_point(sp, asoc->pmtu); /* 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 <= 2*MTU. * * o The initial value of ssthresh MAY be arbitrarily high * (for example, implementations MAY use the size of the * receiver advertised window). */ peer->cwnd = asoc->pmtu * 2; /* 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; peer->error_threshold = peer->max_retrans; /* By default, enable heartbeat for peer address. */ peer->hb_allowed = 1; /* Initialize the peer's heartbeat interval based on the * sock configured value. */ peer->hb_interval = msecs_to_jiffies(sp->paddrparam.spp_hbinterval); /* Set the path max_retrans. */ peer->max_retrans = asoc->max_retrans; /* Set the transport's RTO.initial value */ peer->rto = asoc->rto_initial; /* Attach the remote transport to our asoc. */ list_add_tail(&peer->transports, &asoc->peer.transport_addr_list); /* 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 *peer = NULL; 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)) { peer = transport; list_del(pos); break; } } /* The address we want delete is not in the association. */ if (!peer) return; /* 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.retran_path == peer) asoc->peer.retran_path = transport; if (asoc->peer.last_data_from == peer) asoc->peer.last_data_from = transport; sctp_transport_free(peer);}/* 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;}/* 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 list_head *pos; int spc_state = 0; /* Record the transition on the transport. */ switch (command) { case SCTP_TRANSPORT_UP: transport->active = SCTP_ACTIVE; spc_state = SCTP_ADDR_AVAILABLE; break; case SCTP_TRANSPORT_DOWN: transport->active = SCTP_INACTIVE; spc_state = SCTP_ADDR_UNREACHABLE; break; default: return; }; /* Generate and send a SCTP_PEER_ADDR_CHANGE notification to the * user. */ event = sctp_ulpevent_make_peer_addr_change(asoc, (struct sockaddr_storage *) &transport->ipaddr, 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->active) 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->active && 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;}/* Allocate 'num' TSNs by incrementing the association's TSN by num. */__u32 sctp_association_get_tsn_block(struct sctp_association *asoc, int num){ __u32 retval = asoc->next_tsn; asoc->next_tsn += num; asoc->unack_data += num; 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;}/* Use this function for the packet prepend callback when no ECNE * packet is desired (e.g. some packets don't like to be bundled). */struct sctp_chunk *sctp_get_no_prepend(struct sctp_association *asoc){ return NULL;}/* * 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; __u32 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -