?? associola.c
字號:
/* 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 + -