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