X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=net%2Fsctp%2Fassociola.c;h=f4b23043b610b85a7a004770569fa5a01e0e932a;hb=759af00ebef858015eb68876ac1f383bcb6a1774;hp=ad0057db0f91884b39a393b06759bdc383c9776b;hpb=4c1ac1b49122b805adfa4efc620592f68dccf5db;p=safe%2Fjmp%2Flinux-2.6 diff --git a/net/sctp/associola.c b/net/sctp/associola.c index ad0057d..f4b2304 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -1,21 +1,21 @@ -/* SCTP kernel reference Implementation +/* SCTP kernel implementation * (C) Copyright IBM Corp. 2001, 2004 * Copyright (c) 1999-2000 Cisco, Inc. * Copyright (c) 1999-2001 Motorola, Inc. * Copyright (c) 2001 Intel Corp. * Copyright (c) 2001 La Monte H.P. Yarroll * - * This file is part of the SCTP kernel reference Implementation + * This file is part of the SCTP kernel implementation * * This module provides the abstraction for an SCTP association. * - * The SCTP reference implementation is free software; + * This SCTP implementation is free software; * you can redistribute it and/or modify it under the terms of * the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * - * The SCTP reference implementation is distributed in the hope that it + * This SCTP implementation is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * ************************ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. @@ -52,7 +52,6 @@ #include #include #include -#include #include #include @@ -62,6 +61,7 @@ /* Forward declarations for internal functions. */ static void sctp_assoc_bh_rcv(struct work_struct *work); +static void sctp_assoc_free_asconf_acks(struct sctp_association *asoc); /* 1st Level Abstractions. */ @@ -75,6 +75,8 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a { struct sctp_sock *sp; int i; + sctp_paramhdr_t *p; + int err; /* Retrieve the SCTP per socket area. */ sp = sctp_sk((struct sock *)sk); @@ -100,7 +102,6 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a /* Initialize the bind addr area. */ sctp_bind_addr_init(&asoc->base.bind_addr, ep->base.bind_addr.port); - rwlock_init(&asoc->base.addr_lock); asoc->state = SCTP_STATE_CLOSED; @@ -135,6 +136,7 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a /* Set association default SACK delay */ asoc->sackdelay = msecs_to_jiffies(sp->sackdelay); + asoc->sackfreq = sp->sackfreq; /* Set the association default flags controlling * Heartbeat, SACK delay, and Path MTU Discovery. @@ -144,7 +146,7 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a /* Initialize the maximum mumber of new data packets that can be sent * in a burst. */ - asoc->max_burst = sctp_max_burst; + asoc->max_burst = sp->max_burst; /* initialize association timers */ asoc->timeouts[SCTP_EVENT_TIMEOUT_NONE] = 0; @@ -158,20 +160,18 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a * If the 'T5-shutdown-guard' timer is used, it SHOULD be set to the * recommended value of 5 times 'RTO.Max'. */ - asoc->timeouts[SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD] + asoc->timeouts[SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD] = 5 * asoc->rto_max; asoc->timeouts[SCTP_EVENT_TIMEOUT_HEARTBEAT] = 0; asoc->timeouts[SCTP_EVENT_TIMEOUT_SACK] = asoc->sackdelay; asoc->timeouts[SCTP_EVENT_TIMEOUT_AUTOCLOSE] = sp->autoclose * HZ; - + /* Initilizes the timers */ - for (i = SCTP_EVENT_TIMEOUT_NONE; i < SCTP_NUM_TIMEOUT_TYPES; ++i) { - init_timer(&asoc->timers[i]); - asoc->timers[i].function = sctp_timer_events[i]; - asoc->timers[i].data = (unsigned long) asoc; - } + for (i = SCTP_EVENT_TIMEOUT_NONE; i < SCTP_NUM_TIMEOUT_TYPES; ++i) + setup_timer(&asoc->timers[i], sctp_timer_events[i], + (unsigned long)asoc); /* Pull default initialization values from the sock options. * Note: This assumes that the values have already been @@ -244,6 +244,7 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a asoc->addip_serial = asoc->c.initial_tsn; INIT_LIST_HEAD(&asoc->addip_chunk_list); + INIT_LIST_HEAD(&asoc->asconf_ack_list); /* Make an empty list of remote transport addresses. */ INIT_LIST_HEAD(&asoc->peer.transport_addr_list); @@ -261,11 +262,16 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a * already received one packet.] */ asoc->peer.sack_needed = 1; + asoc->peer.sack_cnt = 0; - /* Assume that the peer recongizes ASCONF until reported otherwise - * via an ERROR chunk. + /* Assume that the peer will tell us if he recognizes ASCONF + * as part of INIT exchange. + * The sctp_addip_noauth option is there for backward compatibilty + * and will revert old behavior. */ - asoc->peer.asconf_capable = 1; + asoc->peer.asconf_capable = 0; + if (sctp_addip_noauth) + asoc->peer.asconf_capable = 1; /* Create an input queue. */ sctp_inq_init(&asoc->base.inqueue); @@ -277,8 +283,7 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a if (!sctp_ulpq_init(&asoc->ulpq, asoc)) goto fail_init; - /* Set up the tsn tracking. */ - sctp_tsnmap_init(&asoc->peer.tsn_map, SCTP_TSN_MAP_SIZE, 0); + memset(&asoc->peer.tsn_map, 0, sizeof(struct sctp_tsnmap)); asoc->need_ecne = 0; @@ -298,6 +303,31 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a asoc->default_flags = sp->default_flags; asoc->default_context = sp->default_context; asoc->default_timetolive = sp->default_timetolive; + asoc->default_rcv_context = sp->default_rcv_context; + + /* AUTH related initializations */ + INIT_LIST_HEAD(&asoc->endpoint_shared_keys); + err = sctp_auth_asoc_copy_shkeys(ep, asoc, gfp); + if (err) + goto fail_init; + + asoc->active_key_id = ep->active_key_id; + asoc->asoc_shared_key = NULL; + + asoc->default_hmac_id = 0; + /* Save the hmacs and chunks list into this association */ + if (ep->auth_hmacs_list) + memcpy(asoc->c.auth_hmacs, ep->auth_hmacs_list, + ntohs(ep->auth_hmacs_list->param_hdr.length)); + if (ep->auth_chunk_list) + memcpy(asoc->c.auth_chunks, ep->auth_chunk_list, + ntohs(ep->auth_chunk_list->param_hdr.length)); + + /* Get the AUTH random number for this association */ + p = (sctp_paramhdr_t *)asoc->c.auth_random; + p->type = SCTP_PARAM_RANDOM; + p->length = htons(sizeof(sctp_paramhdr_t) + SCTP_AUTH_RANDOM_LENGTH); + get_random_bytes(p+1, SCTP_AUTH_RANDOM_LENGTH); return asoc; @@ -371,6 +401,8 @@ void sctp_association_free(struct sctp_association *asoc) /* Dispose of any pending chunks on the inqueue. */ sctp_inq_free(&asoc->base.inqueue); + sctp_tsnmap_free(&asoc->peer.tsn_map); + /* Free ssnmap storage. */ sctp_ssnmap_free(asoc->ssnmap); @@ -390,6 +422,9 @@ void sctp_association_free(struct sctp_association *asoc) /* Free peer's cached cookie. */ kfree(asoc->peer.cookie); + kfree(asoc->peer.peer_random); + kfree(asoc->peer.peer_chunks); + kfree(asoc->peer.peer_hmacs); /* Release the transport structures. */ list_for_each_safe(pos, temp, &asoc->peer.transport_addr_list) { @@ -401,13 +436,18 @@ void sctp_association_free(struct sctp_association *asoc) asoc->peer.transport_count = 0; /* Free any cached ASCONF_ACK chunk. */ - if (asoc->addip_last_asconf_ack) - sctp_chunk_free(asoc->addip_last_asconf_ack); + sctp_assoc_free_asconf_acks(asoc); /* Free any cached ASCONF chunk. */ if (asoc->addip_last_asconf) sctp_chunk_free(asoc->addip_last_asconf); + /* AUTH - Free the endpoint shared keys */ + sctp_auth_destroy_keys(&asoc->endpoint_shared_keys); + + /* AUTH - Free the association shared key */ + sctp_auth_key_put(asoc->asoc_shared_key); + sctp_association_put(asoc); } @@ -425,7 +465,7 @@ static void sctp_association_destroy(struct sctp_association *asoc) spin_unlock_bh(&sctp_assocs_id_lock); } - BUG_TRAP(!atomic_read(&asoc->rmem_alloc)); + WARN_ON(atomic_read(&asoc->rmem_alloc)); if (asoc->base.malloced) { kfree(asoc); @@ -437,6 +477,15 @@ static void sctp_association_destroy(struct sctp_association *asoc) void sctp_assoc_set_primary(struct sctp_association *asoc, struct sctp_transport *transport) { + int changeover = 0; + + /* it's a changeover only if we already have a primary path + * that we are changing + */ + if (asoc->peer.primary_path != NULL && + asoc->peer.primary_path != transport) + changeover = 1 ; + asoc->peer.primary_path = transport; /* Set a default msg_name for events. */ @@ -462,12 +511,12 @@ void sctp_assoc_set_primary(struct sctp_association *asoc, * double switch to the same destination address. */ if (transport->cacc.changeover_active) - transport->cacc.cycling_changeover = 1; + transport->cacc.cycling_changeover = changeover; /* 2) The sender MUST set CHANGEOVER_ACTIVE to indicate that * a changeover has occurred. */ - transport->cacc.changeover_active = 1; + transport->cacc.changeover_active = changeover; /* 3) The sender MUST store the next TSN to be sent in * next_tsn_at_change. @@ -551,11 +600,12 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc, /* Check to see if this is a duplicate. */ peer = sctp_assoc_lookup_paddr(asoc, addr); if (peer) { + /* An UNKNOWN state is only set on transports added by + * user in sctp_connectx() call. Such transports should be + * considered CONFIRMED per RFC 4960, Section 5.4. + */ if (peer->state == SCTP_UNKNOWN) { - if (peer_state == SCTP_ACTIVE) - peer->state = SCTP_ACTIVE; - if (peer_state == SCTP_UNCONFIRMED) - peer->state = SCTP_UNCONFIRMED; + peer->state = SCTP_ACTIVE; } return peer; } @@ -578,6 +628,7 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc, * association configured value. */ peer->sackdelay = asoc->sackdelay; + peer->sackfreq = asoc->sackfreq; /* Enable/disable heartbeat, SACK delay, and path MTU discovery * based on association setting. @@ -604,6 +655,7 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc, SCTP_DEBUG_PRINTK("sctp_assoc_add_peer:association %p PMTU set to " "%d\n", asoc, asoc->pathmtu); + peer->pmtu_pending = 0; asoc->frag_point = sctp_frag_point(sp, asoc->pathmtu); @@ -681,12 +733,11 @@ struct sctp_transport *sctp_assoc_lookup_paddr( 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); + list_for_each_entry(t, &asoc->peer.transport_addr_list, + transports) { if (sctp_cmp_addr_exact(address, &t->ipaddr)) return t; } @@ -694,6 +745,23 @@ struct sctp_transport *sctp_assoc_lookup_paddr( 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. @@ -708,24 +776,36 @@ void sctp_assoc_control_transport(struct sctp_association *asoc, 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; - spc_state = SCTP_ADDR_AVAILABLE; break; case SCTP_TRANSPORT_DOWN: - transport->state = SCTP_INACTIVE; + /* 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. @@ -747,8 +827,8 @@ void sctp_assoc_control_transport(struct sctp_association *asoc, */ first = NULL; second = NULL; - list_for_each(pos, &asoc->peer.transport_addr_list) { - t = list_entry(pos, struct sctp_transport, transports); + list_for_each_entry(t, &asoc->peer.transport_addr_list, + transports) { if ((t->state == SCTP_INACTIVE) || (t->state == SCTP_UNCONFIRMED)) @@ -865,7 +945,6 @@ struct sctp_transport *sctp_assoc_lookup_tsn(struct sctp_association *asoc, { 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); @@ -889,8 +968,8 @@ struct sctp_transport *sctp_assoc_lookup_tsn(struct sctp_association *asoc, active = asoc->peer.active_path; - list_for_each(entry, &active->transmitted) { - chunk = list_entry(entry, struct sctp_chunk, transmitted_list); + list_for_each_entry(chunk, &active->transmitted, + transmitted_list) { if (key == chunk->subh.data_hdr->tsn) { match = active; @@ -899,14 +978,13 @@ struct sctp_transport *sctp_assoc_lookup_tsn(struct sctp_association *asoc, } /* 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); + list_for_each_entry(transport, &asoc->peer.transport_addr_list, + transports) { if (transport == active) break; - list_for_each(entry, &transport->transmitted) { - chunk = list_entry(entry, struct sctp_chunk, - transmitted_list); + list_for_each_entry(chunk, &transport->transmitted, + transmitted_list) { if (key == chunk->subh.data_hdr->tsn) { match = transport; goto out; @@ -924,8 +1002,6 @@ struct sctp_transport *sctp_assoc_is_match(struct sctp_association *asoc, { struct sctp_transport *transport; - sctp_read_lock(&asoc->base.addr_lock); - 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); @@ -939,7 +1015,6 @@ struct sctp_transport *sctp_assoc_is_match(struct sctp_association *asoc, transport = NULL; out: - sctp_read_unlock(&asoc->base.addr_lock); return transport; } @@ -967,6 +1042,16 @@ static void sctp_assoc_bh_rcv(struct work_struct *work) 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. */ @@ -1038,14 +1123,17 @@ void sctp_assoc_update(struct sctp_association *asoc, 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); + sctp_tsnmap_init(&asoc->peer.tsn_map, SCTP_TSN_MAP_INITIAL, + asoc->peer.i.initial_tsn, GFP_ATOMIC); /* 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 @@ -1063,11 +1151,22 @@ void sctp_assoc_update(struct sctp_association *asoc, */ 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); + list_for_each_entry(trans, &new->peer.transport_addr_list, + transports) { if (!sctp_assoc_lookup_paddr(asoc, &trans->ipaddr)) sctp_assoc_add_peer(asoc, &trans->ipaddr, GFP_ATOMIC, trans->state); @@ -1080,7 +1179,32 @@ void sctp_assoc_update(struct sctp_association *asoc, 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. @@ -1094,6 +1218,9 @@ void sctp_assoc_update_retran_path(struct sctp_association *asoc) struct list_head *head = &asoc->peer.transport_addr_list; struct list_head *pos; + if (asoc->peer.transport_count == 1) + return; + /* Find the next transport in a round-robin fashion. */ t = asoc->peer.retran_path; pos = &t->transports; @@ -1108,6 +1235,15 @@ void sctp_assoc_update_retran_path(struct sctp_association *asoc) t = list_entry(pos, struct sctp_transport, transports); + /* 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; + } + /* Try to find an active transport. */ if ((t->state == SCTP_ACTIVE) || @@ -1120,15 +1256,6 @@ void sctp_assoc_update_retran_path(struct sctp_association *asoc) 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; @@ -1192,15 +1319,18 @@ struct sctp_transport *sctp_assoc_choose_shutdown_transport( 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); + list_for_each_entry(t, &asoc->peer.transport_addr_list, + 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; } @@ -1212,7 +1342,7 @@ void sctp_assoc_sync_pmtu(struct sctp_association *asoc) } SCTP_DEBUG_PRINTK("%s: asoc:%p, pmtu:%d, frag_point:%d\n", - __FUNCTION__, asoc, asoc->pathmtu, asoc->frag_point); + __func__, asoc, asoc->pathmtu, asoc->frag_point); } /* Should we send a SACK to update our peer? */ @@ -1252,7 +1382,7 @@ void sctp_assoc_rwnd_increase(struct sctp_association *asoc, unsigned len) } SCTP_DEBUG_PRINTK("%s: asoc %p rwnd increased by %d to (%u, %u) " - "- %u\n", __FUNCTION__, asoc, len, asoc->rwnd, + "- %u\n", __func__, asoc, len, asoc->rwnd, asoc->rwnd_over, asoc->a_rwnd); /* Send a window update SACK if the rwnd has increased by at least the @@ -1263,7 +1393,7 @@ void sctp_assoc_rwnd_increase(struct sctp_association *asoc, unsigned len) 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__, + "rwnd: %u a_rwnd: %u\n", __func__, asoc, asoc->rwnd, asoc->a_rwnd); sack = sctp_make_sack(asoc); if (!sack) @@ -1292,7 +1422,7 @@ void sctp_assoc_rwnd_decrease(struct sctp_association *asoc, unsigned len) asoc->rwnd = 0; } SCTP_DEBUG_PRINTK("%s: asoc %p rwnd decreased by %d to (%u, %u)\n", - __FUNCTION__, asoc, len, asoc->rwnd, + __func__, asoc, len, asoc->rwnd, asoc->rwnd_over); } @@ -1333,22 +1463,91 @@ int sctp_assoc_set_bind_addr_from_cookie(struct sctp_association *asoc, 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, +/* 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; + int found = 0; - sctp_read_lock(&asoc->base.addr_lock); 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))) { + sctp_sk(asoc->base.sk))) found = 1; - goto out; - } - found = 0; -out: - sctp_read_unlock(&asoc->base.addr_lock); 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; +}