diff options
Diffstat (limited to 'net/sctp')
-rw-r--r-- | net/sctp/associola.c | 97 | ||||
-rw-r--r-- | net/sctp/bind_addr.c | 21 | ||||
-rw-r--r-- | net/sctp/chunk.c | 62 | ||||
-rw-r--r-- | net/sctp/debug.c | 14 | ||||
-rw-r--r-- | net/sctp/ipv6.c | 2 | ||||
-rw-r--r-- | net/sctp/output.c | 190 | ||||
-rw-r--r-- | net/sctp/outqueue.c | 57 | ||||
-rw-r--r-- | net/sctp/proc.c | 4 | ||||
-rw-r--r-- | net/sctp/protocol.c | 20 | ||||
-rw-r--r-- | net/sctp/sm_make_chunk.c | 25 | ||||
-rw-r--r-- | net/sctp/sm_sideeffect.c | 57 | ||||
-rw-r--r-- | net/sctp/sm_statefuns.c | 83 | ||||
-rw-r--r-- | net/sctp/socket.c | 165 | ||||
-rw-r--r-- | net/sctp/sysctl.c | 12 | ||||
-rw-r--r-- | net/sctp/transport.c | 11 |
15 files changed, 548 insertions, 272 deletions
diff --git a/net/sctp/associola.c b/net/sctp/associola.c index 525864bf4f0..7eed77a39d0 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -112,6 +112,7 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a asoc->cookie_life.tv_usec = (sp->assocparams.sasoc_cookie_life % 1000) * 1000; asoc->frag_point = 0; + asoc->user_frag = sp->user_frag; /* Set the association max_retrans and RTO values from the * socket values. @@ -202,6 +203,7 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a asoc->a_rwnd = asoc->rwnd; asoc->rwnd_over = 0; + asoc->rwnd_press = 0; /* Use my own max window until I learn something better. */ asoc->peer.rwnd = SCTP_DEFAULT_MAXWINDOW; @@ -582,6 +584,33 @@ void sctp_assoc_rm_peer(struct sctp_association *asoc, asoc->addip_last_asconf->transport == peer) asoc->addip_last_asconf->transport = NULL; + /* If we have something on the transmitted list, we have to + * save it off. The best place is the active path. + */ + if (!list_empty(&peer->transmitted)) { + struct sctp_transport *active = asoc->peer.active_path; + struct sctp_chunk *ch; + + /* Reset the transport of each chunk on this list */ + list_for_each_entry(ch, &peer->transmitted, + transmitted_list) { + ch->transport = NULL; + ch->rtt_in_progress = 0; + } + + list_splice_tail_init(&peer->transmitted, + &active->transmitted); + + /* Start a T3 timer here in case it wasn't running so + * that these migrated packets have a chance to get + * retrnasmitted. + */ + if (!timer_pending(&active->T3_rtx_timer)) + if (!mod_timer(&active->T3_rtx_timer, + jiffies + active->rto)) + sctp_transport_hold(active); + } + asoc->peer.transport_count--; sctp_transport_free(peer); @@ -651,13 +680,15 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc, */ peer->param_flags = asoc->param_flags; + sctp_transport_route(peer, NULL, sp); + /* 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 (peer->param_flags & SPP_PMTUD_DISABLE) { + 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. @@ -673,7 +704,7 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc, "%d\n", asoc, asoc->pathmtu); peer->pmtu_pending = 0; - asoc->frag_point = sctp_frag_point(sp, asoc->pathmtu); + asoc->frag_point = sctp_frag_point(asoc, asoc->pathmtu); /* The asoc->peer.port might not be meaningful yet, but * initialize the packet structure anyway. @@ -810,11 +841,16 @@ void sctp_assoc_control_transport(struct sctp_association *asoc, break; case SCTP_TRANSPORT_DOWN: - /* if the transort was never confirmed, do not transition it - * to inactive state. + /* If the transport was never confirmed, do not transition it + * to inactive state. Also, release the cached route since + * there may be a better route next time. */ if (transport->state != SCTP_UNCONFIRMED) transport->state = SCTP_INACTIVE; + else { + dst_release(transport->dst); + transport->dst = NULL; + } spc_state = SCTP_ADDR_UNREACHABLE; break; @@ -1324,9 +1360,8 @@ void sctp_assoc_sync_pmtu(struct sctp_association *asoc) } if (pmtu) { - struct sctp_sock *sp = sctp_sk(asoc->base.sk); asoc->pathmtu = pmtu; - asoc->frag_point = sctp_frag_point(sp, pmtu); + asoc->frag_point = sctp_frag_point(asoc, pmtu); } SCTP_DEBUG_PRINTK("%s: asoc:%p, pmtu:%d, frag_point:%d\n", @@ -1369,6 +1404,17 @@ void sctp_assoc_rwnd_increase(struct sctp_association *asoc, unsigned len) asoc->rwnd += len; } + /* If we had window pressure, start recovering it + * once our rwnd had reached the accumulated pressure + * threshold. The idea is to recover slowly, but up + * to the initial advertised window. + */ + if (asoc->rwnd_press && asoc->rwnd >= asoc->rwnd_press) { + int change = min(asoc->pathmtu, asoc->rwnd_press); + asoc->rwnd += change; + asoc->rwnd_press -= change; + } + SCTP_DEBUG_PRINTK("%s: asoc %p rwnd increased by %d to (%u, %u) " "- %u\n", __func__, asoc, len, asoc->rwnd, asoc->rwnd_over, asoc->a_rwnd); @@ -1401,32 +1447,51 @@ void sctp_assoc_rwnd_increase(struct sctp_association *asoc, unsigned len) /* Decrease asoc's rwnd by len. */ void sctp_assoc_rwnd_decrease(struct sctp_association *asoc, unsigned len) { + int rx_count; + int over = 0; + SCTP_ASSERT(asoc->rwnd, "rwnd zero", return); SCTP_ASSERT(!asoc->rwnd_over, "rwnd_over not zero", return); + + if (asoc->ep->rcvbuf_policy) + rx_count = atomic_read(&asoc->rmem_alloc); + else + rx_count = atomic_read(&asoc->base.sk->sk_rmem_alloc); + + /* If we've reached or overflowed our receive buffer, announce + * a 0 rwnd if rwnd would still be positive. Store the + * the pottential pressure overflow so that the window can be restored + * back to original value. + */ + if (rx_count >= asoc->base.sk->sk_rcvbuf) + over = 1; + if (asoc->rwnd >= len) { asoc->rwnd -= len; + if (over) { + asoc->rwnd_press = asoc->rwnd; + asoc->rwnd = 0; + } } else { asoc->rwnd_over = len - asoc->rwnd; asoc->rwnd = 0; } - SCTP_DEBUG_PRINTK("%s: asoc %p rwnd decreased by %d to (%u, %u)\n", + SCTP_DEBUG_PRINTK("%s: asoc %p rwnd decreased by %d to (%u, %u, %u)\n", __func__, asoc, len, asoc->rwnd, - asoc->rwnd_over); + asoc->rwnd_over, asoc->rwnd_press); } /* 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, 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; diff --git a/net/sctp/bind_addr.c b/net/sctp/bind_addr.c index 6d5944a745d..13a6fba4107 100644 --- a/net/sctp/bind_addr.c +++ b/net/sctp/bind_addr.c @@ -510,9 +510,28 @@ int sctp_in_scope(const union sctp_addr *addr, sctp_scope_t scope) * of requested destination address, sender and receiver * SHOULD include all of its addresses with level greater * than or equal to L. + * + * Address scoping can be selectively controlled via sysctl + * option */ - if (addr_scope <= scope) + switch (sctp_scope_policy) { + case SCTP_SCOPE_POLICY_DISABLE: return 1; + case SCTP_SCOPE_POLICY_ENABLE: + if (addr_scope <= scope) + return 1; + break; + case SCTP_SCOPE_POLICY_PRIVATE: + if (addr_scope <= scope || SCTP_SCOPE_PRIVATE == addr_scope) + return 1; + break; + case SCTP_SCOPE_POLICY_LINK: + if (addr_scope <= scope || SCTP_SCOPE_LINK == addr_scope) + return 1; + break; + default: + break; + } return 0; } diff --git a/net/sctp/chunk.c b/net/sctp/chunk.c index 1748ef90950..acf7c4d128f 100644 --- a/net/sctp/chunk.c +++ b/net/sctp/chunk.c @@ -59,6 +59,7 @@ static void sctp_datamsg_init(struct sctp_datamsg *msg) msg->can_abandon = 0; msg->expires_at = 0; INIT_LIST_HEAD(&msg->chunks); + msg->msg_size = 0; } /* Allocate and initialize datamsg. */ @@ -73,6 +74,19 @@ SCTP_STATIC struct sctp_datamsg *sctp_datamsg_new(gfp_t gfp) return msg; } +void sctp_datamsg_free(struct sctp_datamsg *msg) +{ + struct sctp_chunk *chunk; + + /* This doesn't have to be a _safe vairant because + * sctp_chunk_free() only drops the refs. + */ + list_for_each_entry(chunk, &msg->chunks, frag_list) + sctp_chunk_free(chunk); + + sctp_datamsg_put(msg); +} + /* Final destructruction of datamsg memory. */ static void sctp_datamsg_destroy(struct sctp_datamsg *msg) { @@ -142,6 +156,7 @@ static void sctp_datamsg_assign(struct sctp_datamsg *msg, struct sctp_chunk *chu { sctp_datamsg_hold(msg); chunk->msg = msg; + msg->msg_size += chunk->skb->len; } @@ -158,6 +173,7 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc, { int max, whole, i, offset, over, err; int len, first_len; + int max_data; struct sctp_chunk *chunk; struct sctp_datamsg *msg; struct list_head *pos, *temp; @@ -179,8 +195,14 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc, __func__, msg, msg->expires_at, jiffies); } - max = asoc->frag_point; + /* This is the biggest possible DATA chunk that can fit into + * the packet + */ + max_data = asoc->pathmtu - + sctp_sk(asoc->base.sk)->pf->af->net_header_len - + sizeof(struct sctphdr) - sizeof(struct sctp_data_chunk); + max = asoc->frag_point; /* If the the peer requested that we authenticate DATA chunks * we need to accound for bundling of the AUTH chunks along with * DATA. @@ -189,23 +211,41 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc, struct sctp_hmac *hmac_desc = sctp_auth_asoc_get_hmac(asoc); if (hmac_desc) - max -= WORD_ROUND(sizeof(sctp_auth_chunk_t) + + max_data -= WORD_ROUND(sizeof(sctp_auth_chunk_t) + hmac_desc->hmac_len); } + /* Now, check if we need to reduce our max */ + if (max > max_data) + max = max_data; + whole = 0; first_len = max; + /* Check to see if we have a pending SACK and try to let it be bundled + * with this message. Do this if we don't have any data queued already. + * To check that, look at out_qlen and retransmit list. + * NOTE: we will not reduce to account for SACK, if the message would + * not have been fragmented. + */ + if (timer_pending(&asoc->timers[SCTP_EVENT_TIMEOUT_SACK]) && + asoc->outqueue.out_qlen == 0 && + list_empty(&asoc->outqueue.retransmit) && + msg_len > max) + max_data -= WORD_ROUND(sizeof(sctp_sack_chunk_t)); + /* Encourage Cookie-ECHO bundling. */ - if (asoc->state < SCTP_STATE_COOKIE_ECHOED) { - whole = msg_len / (max - SCTP_ARBITRARY_COOKIE_ECHO_LEN); - - /* Account for the DATA to be bundled with the COOKIE-ECHO. */ - if (whole) { - first_len = max - SCTP_ARBITRARY_COOKIE_ECHO_LEN; - msg_len -= first_len; - whole = 1; - } + if (asoc->state < SCTP_STATE_COOKIE_ECHOED) + max_data -= SCTP_ARBITRARY_COOKIE_ECHO_LEN; + + /* Now that we adjusted completely, reset first_len */ + if (first_len > max_data) + first_len = max_data; + + /* Account for a different sized first fragment */ + if (msg_len >= first_len) { + msg_len -= first_len; + whole = 1; } /* How many full sized? How many bytes leftover? */ diff --git a/net/sctp/debug.c b/net/sctp/debug.c index 7ff548a30cf..bf24fa697de 100644 --- a/net/sctp/debug.c +++ b/net/sctp/debug.c @@ -52,7 +52,7 @@ int sctp_debug_flag = 1; /* Initially enable DEBUG */ #endif /* SCTP_DEBUG */ /* These are printable forms of Chunk ID's from section 3.1. */ -static const char *sctp_cid_tbl[SCTP_NUM_BASE_CHUNK_TYPES] = { +static const char *const sctp_cid_tbl[SCTP_NUM_BASE_CHUNK_TYPES] = { "DATA", "INIT", "INIT_ACK", @@ -97,7 +97,7 @@ const char *sctp_cname(const sctp_subtype_t cid) } /* These are printable forms of the states. */ -const char *sctp_state_tbl[SCTP_STATE_NUM_STATES] = { +const char *const sctp_state_tbl[SCTP_STATE_NUM_STATES] = { "STATE_EMPTY", "STATE_CLOSED", "STATE_COOKIE_WAIT", @@ -110,7 +110,7 @@ const char *sctp_state_tbl[SCTP_STATE_NUM_STATES] = { }; /* Events that could change the state of an association. */ -const char *sctp_evttype_tbl[] = { +const char *const sctp_evttype_tbl[] = { "EVENT_T_unknown", "EVENT_T_CHUNK", "EVENT_T_TIMEOUT", @@ -119,7 +119,7 @@ const char *sctp_evttype_tbl[] = { }; /* Return value of a state function */ -const char *sctp_status_tbl[] = { +const char *const sctp_status_tbl[] = { "DISPOSITION_DISCARD", "DISPOSITION_CONSUME", "DISPOSITION_NOMEM", @@ -132,7 +132,7 @@ const char *sctp_status_tbl[] = { }; /* Printable forms of primitives */ -static const char *sctp_primitive_tbl[SCTP_NUM_PRIMITIVE_TYPES] = { +static const char *const sctp_primitive_tbl[SCTP_NUM_PRIMITIVE_TYPES] = { "PRIMITIVE_ASSOCIATE", "PRIMITIVE_SHUTDOWN", "PRIMITIVE_ABORT", @@ -149,7 +149,7 @@ const char *sctp_pname(const sctp_subtype_t id) return "unknown_primitive"; } -static const char *sctp_other_tbl[] = { +static const char *const sctp_other_tbl[] = { "NO_PENDING_TSN", "ICMP_PROTO_UNREACH", }; @@ -162,7 +162,7 @@ const char *sctp_oname(const sctp_subtype_t id) return "unknown 'other' event"; } -static const char *sctp_timer_tbl[] = { +static const char *const sctp_timer_tbl[] = { "TIMEOUT_NONE", "TIMEOUT_T1_COOKIE", "TIMEOUT_T1_INIT", diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index 6a4b1909414..bb280e60e00 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c @@ -949,7 +949,7 @@ static int sctp6_rcv(struct sk_buff *skb) return sctp_rcv(skb) ? -1 : 0; } -static struct inet6_protocol sctpv6_protocol = { +static const struct inet6_protocol sctpv6_protocol = { .handler = sctp6_rcv, .err_handler = sctp_v6_err, .flags = INET6_PROTO_NOPOLICY | INET6_PROTO_FINAL, diff --git a/net/sctp/output.c b/net/sctp/output.c index b7641144451..5cbda8f1ddf 100644 --- a/net/sctp/output.c +++ b/net/sctp/output.c @@ -61,8 +61,24 @@ #include <net/sctp/checksum.h> /* Forward declarations for private helpers. */ -static sctp_xmit_t sctp_packet_append_data(struct sctp_packet *packet, +static sctp_xmit_t sctp_packet_can_append_data(struct sctp_packet *packet, struct sctp_chunk *chunk); +static void sctp_packet_append_data(struct sctp_packet *packet, + struct sctp_chunk *chunk); +static sctp_xmit_t sctp_packet_will_fit(struct sctp_packet *packet, + struct sctp_chunk *chunk, + u16 chunk_len); + +static void sctp_packet_reset(struct sctp_packet *packet) +{ + packet->size = packet->overhead; + packet->has_cookie_echo = 0; + packet->has_sack = 0; + packet->has_data = 0; + packet->has_auth = 0; + packet->ipfragok = 0; + packet->auth = NULL; +} /* Config a packet. * This appears to be a followup set of initializations. @@ -75,13 +91,8 @@ struct sctp_packet *sctp_packet_config(struct sctp_packet *packet, SCTP_DEBUG_PRINTK("%s: packet:%p vtag:0x%x\n", __func__, packet, vtag); + sctp_packet_reset(packet); packet->vtag = vtag; - packet->has_cookie_echo = 0; - packet->has_sack = 0; - packet->has_auth = 0; - packet->has_data = 0; - packet->ipfragok = 0; - packet->auth = NULL; if (ecn_capable && sctp_packet_empty(packet)) { chunk = sctp_get_ecne_prepend(packet->transport->asoc); @@ -119,15 +130,9 @@ struct sctp_packet *sctp_packet_init(struct sctp_packet *packet, } overhead += sizeof(struct sctphdr); packet->overhead = overhead; - packet->size = overhead; + sctp_packet_reset(packet); packet->vtag = 0; - packet->has_cookie_echo = 0; - packet->has_sack = 0; - packet->has_auth = 0; - packet->has_data = 0; - packet->ipfragok = 0; packet->malloced = 0; - packet->auth = NULL; return packet; } @@ -204,7 +209,7 @@ static sctp_xmit_t sctp_packet_bundle_auth(struct sctp_packet *pkt, /* See if this is an auth chunk we are bundling or if * auth is already bundled. */ - if (chunk->chunk_hdr->type == SCTP_CID_AUTH || pkt->auth) + if (chunk->chunk_hdr->type == SCTP_CID_AUTH || pkt->has_auth) return retval; /* if the peer did not request this chunk to be authenticated, @@ -234,18 +239,19 @@ static sctp_xmit_t sctp_packet_bundle_sack(struct sctp_packet *pkt, if (sctp_chunk_is_data(chunk) && !pkt->has_sack && !pkt->has_cookie_echo) { struct sctp_association *asoc; + struct timer_list *timer; asoc = pkt->transport->asoc; + timer = &asoc->timers[SCTP_EVENT_TIMEOUT_SACK]; - if (asoc->a_rwnd > asoc->rwnd) { + /* If the SACK timer is running, we have a pending SACK */ + if (timer_pending(timer)) { struct sctp_chunk *sack; asoc->a_rwnd = asoc->rwnd; sack = sctp_make_sack(asoc); if (sack) { - struct timer_list *timer; retval = sctp_packet_append_chunk(pkt, sack); asoc->peer.sack_needed = 0; - timer = &asoc->timers[SCTP_EVENT_TIMEOUT_SACK]; - if (timer_pending(timer) && del_timer(timer)) + if (del_timer(timer)) sctp_association_put(asoc); } } @@ -261,13 +267,20 @@ sctp_xmit_t sctp_packet_append_chunk(struct sctp_packet *packet, { sctp_xmit_t retval = SCTP_XMIT_OK; __u16 chunk_len = WORD_ROUND(ntohs(chunk->chunk_hdr->length)); - size_t psize; - size_t pmtu; - int too_big; SCTP_DEBUG_PRINTK("%s: packet:%p chunk:%p\n", __func__, packet, chunk); + /* Data chunks are special. Before seeing what else we can + * bundle into this packet, check to see if we are allowed to + * send this DATA. + */ + if (sctp_chunk_is_data(chunk)) { + retval = sctp_packet_can_append_data(packet, chunk); + if (retval != SCTP_XMIT_OK) + goto finish; + } + /* Try to bundle AUTH chunk */ retval = sctp_packet_bundle_auth(packet, chunk); if (retval != SCTP_XMIT_OK) @@ -278,51 +291,16 @@ sctp_xmit_t sctp_packet_append_chunk(struct sctp_packet *packet, if (retval != SCTP_XMIT_OK) goto finish; - psize = packet->size; - pmtu = ((packet->transport->asoc) ? - (packet->transport->asoc->pathmtu) : - (packet->transport->pathmtu)); - - too_big = (psize + chunk_len > pmtu); - - /* Decide if we need to fragment or resubmit later. */ - if (too_big) { - /* It's OK to fragmet at IP level if any one of the following - * is true: - * 1. The packet is empty (meaning this chunk is greater - * the MTU) - * 2. The chunk we are adding is a control chunk - * 3. The packet doesn't have any data in it yet and data - * requires authentication. - */ - if (sctp_packet_empty(packet) || !sctp_chunk_is_data(chunk) || - (!packet->has_data && chunk->auth)) { - /* We no longer do re-fragmentation. - * Just fragment at the IP layer, if we - * actually hit this condition - */ - packet->ipfragok = 1; - goto append; - - } else { - retval = SCTP_XMIT_PMTU_FULL; - goto finish; - } - } - -append: - /* We believe that this chunk is OK to add to the packet (as - * long as we have the cwnd for it). - */ + /* Check to see if this chunk will fit into the packet */ + retval = sctp_packet_will_fit(packet, chunk, chunk_len); + if (retval != SCTP_XMIT_OK) + goto finish; - /* DATA is a special case since we must examine both rwnd and cwnd - * before we send DATA. - */ + /* We believe that this chunk is OK to add to the packet */ switch (chunk->chunk_hdr->type) { case SCTP_CID_DATA: - retval = sctp_packet_append_data(packet, chunk); - if (SCTP_XMIT_OK != retval) - goto finish; + /* Account for the data being in the packet */ + sctp_packet_append_data(packet, chunk); /* Disallow SACK bundling after DATA. */ packet->has_sack = 1; /* Disallow AUTH bundling after DATA */ @@ -407,7 +385,7 @@ int sctp_packet_transmit(struct sctp_packet *packet) } dst = dst_clone(tp->dst); skb_dst_set(nskb, dst); - if (dst) + if (!dst) goto no_route; /* Build the SCTP header. */ @@ -598,7 +576,7 @@ int sctp_packet_transmit(struct sctp_packet *packet) (*tp->af_specific->sctp_xmit)(nskb, tp); out: - packet->size = packet->overhead; + sctp_packet_reset(packet); return err; no_route: kfree_skb(nskb); @@ -632,16 +610,15 @@ nomem: * 2nd Level Abstractions ********************************************************************/ -/* This private function handles the specifics of appending DATA chunks. */ -static sctp_xmit_t sctp_packet_append_data(struct sctp_packet *packet, +/* This private function check to see if a chunk can be added */ +static sctp_xmit_t sctp_packet_can_append_data(struct sctp_packet *packet, struct sctp_chunk *chunk) { sctp_xmit_t retval = SCTP_XMIT_OK; - size_t datasize, rwnd, inflight; + size_t datasize, rwnd, inflight, flight_size; struct sctp_transport *transport = packet->transport; __u32 max_burst_bytes; struct sctp_association *asoc = transport->asoc; - struct sctp_sock *sp = sctp_sk(asoc->base.sk); struct sctp_outq *q = &asoc->outqueue; /* RFC 2960 6.1 Transmission of DATA Chunks @@ -658,7 +635,8 @@ static sctp_xmit_t sctp_packet_append_data(struct sctp_packet *packet, */ rwnd = asoc->peer.rwnd; - inflight = asoc->outqueue.outstanding_bytes; + inflight = q->outstanding_bytes; + flight_size = transport->flight_size; datasize = sctp_data_size(chunk); @@ -681,8 +659,8 @@ static sctp_xmit_t sctp_packet_append_data(struct sctp_packet *packet, * cwnd = flightsize + Max.Burst * MTU */ max_burst_bytes = asoc->max_burst * asoc->pathmtu; - if ((transport->flight_size + max_burst_bytes) < transport->cwnd) { - transport->cwnd = transport->flight_size + max_burst_bytes; + if ((flight_size + max_burst_bytes) < transport->cwnd) { + transport->cwnd = flight_size + max_burst_bytes; SCTP_DEBUG_PRINTK("%s: cwnd limited by max_burst: " "transport: %p, cwnd: %d, " "ssthresh: %d, flight_size: %d, " @@ -707,7 +685,7 @@ static sctp_xmit_t sctp_packet_append_data(struct sctp_packet *packet, * ignore the value of cwnd and SHOULD NOT delay retransmission. */ if (chunk->fast_retransmit != SCTP_NEED_FRTX) - if (transport->flight_size >= transport->cwnd) { + if (flight_size >= transport->cwnd) { retval = SCTP_XMIT_RWND_FULL; goto finish; } @@ -717,20 +695,36 @@ static sctp_xmit_t sctp_packet_append_data(struct sctp_packet *packet, * if any previously transmitted data on the connection remains * unacknowledged. */ - if (!sp->nodelay && sctp_packet_empty(packet) && - q->outstanding_bytes && sctp_state(asoc, ESTABLISHED)) { - unsigned len = datasize + q->out_qlen; + if (!sctp_sk(asoc->base.sk)->nodelay && sctp_packet_empty(packet) && + inflight && sctp_state(asoc, ESTABLISHED)) { + unsigned max = transport->pathmtu - packet->overhead; + unsigned len = chunk->skb->len + q->out_qlen; /* Check whether this chunk and all the rest of pending * data will fit or delay in hopes of bundling a full * sized packet. + * Don't delay large message writes that may have been + * fragmeneted into small peices. */ - if (len < asoc->frag_point) { + if ((len < max) && (chunk->msg->msg_size < max)) { retval = SCTP_XMIT_NAGLE_DELAY; goto finish; } } +finish: + return retval; +} + +/* This private function does management things when adding DATA chunk */ +static void sctp_packet_append_data(struct sctp_packet *packet, + struct sctp_chunk *chunk) +{ + struct sctp_transport *transport = packet->transport; + size_t datasize = sctp_data_size(chunk); + struct sctp_association *asoc = transport->asoc; + u32 rwnd = asoc->peer.rwnd; + /* Keep track of how many bytes are in flight over this transport. */ transport->flight_size += datasize; @@ -753,7 +747,45 @@ static sctp_xmit_t sctp_packet_append_data(struct sctp_packet *packet, /* Has been accepted for transmission. */ if (!asoc->peer.prsctp_capable) chunk->msg->can_abandon = 0; +} + +static sctp_xmit_t sctp_packet_will_fit(struct sctp_packet *packet, + struct sctp_chunk *chunk, + u16 chunk_len) +{ + size_t psize; + size_t pmtu; + int too_big; + sctp_xmit_t retval = SCTP_XMIT_OK; + + psize = packet->size; + pmtu = ((packet->transport->asoc) ? + (packet->transport->asoc->pathmtu) : + (packet->transport->pathmtu)); + + too_big = (psize + chunk_len > pmtu); + + /* Decide if we need to fragment or resubmit later. */ + if (too_big) { + /* It's OK to fragmet at IP level if any one of the following + * is true: + * 1. The packet is empty (meaning this chunk is greater + * the MTU) + * 2. The chunk we are adding is a control chunk + * 3. The packet doesn't have any data in it yet and data + * requires authentication. + */ + if (sctp_packet_empty(packet) || !sctp_chunk_is_data(chunk) || + (!packet->has_data && chunk->auth)) { + /* We no longer do re-fragmentation. + * Just fragment at the IP layer, if we + * actually hit this condition + */ + packet->ipfragok = 1; + } else { + retval = SCTP_XMIT_PMTU_FULL; + } + } -finish: return retval; } diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c index d765fc53e74..23e5e97aa61 100644 --- a/net/sctp/outqueue.c +++ b/net/sctp/outqueue.c @@ -406,8 +406,9 @@ void sctp_retransmit_mark(struct sctp_outq *q, * not be retransmitted */ if (!chunk->tsn_gap_acked) { - chunk->transport->flight_size -= - sctp_data_size(chunk); + if (chunk->transport) + chunk->transport->flight_size -= + sctp_data_size(chunk); q->outstanding_bytes -= sctp_data_size(chunk); q->asoc->peer.rwnd += (sctp_data_size(chunk) + sizeof(struct sk_buff)); @@ -422,16 +423,6 @@ void sctp_retransmit_mark(struct sctp_outq *q, if ((reason == SCTP_RTXR_FAST_RTX && (chunk->fast_retransmit == SCTP_NEED_FRTX)) || (reason != SCTP_RTXR_FAST_RTX && !chunk->tsn_gap_acked)) { - /* If this chunk was sent less then 1 rto ago, do not - * retransmit this chunk, but give the peer time - * to acknowlege it. Do this only when - * retransmitting due to T3 timeout. - */ - if (reason == SCTP_RTXR_T3_RTX && - time_before(jiffies, chunk->sent_at + - transport->last_rto)) - continue; - /* RFC 2960 6.2.1 Processing a Received SACK * * C) Any time a DATA chunk is marked for @@ -443,7 +434,8 @@ void sctp_retransmit_mark(struct sctp_outq *q, q->asoc->peer.rwnd += (sctp_data_size(chunk) + sizeof(struct sk_buff)); q->outstanding_bytes -= sctp_data_size(chunk); - transport->flight_size -= sctp_data_size(chunk); + if (chunk->transport) + transport->flight_size -= sctp_data_size(chunk); /* sctpimpguide-05 Section 2.8.2 * M5) If a T3-rtx timer expires, the @@ -1310,6 +1302,7 @@ static void sctp_check_transmitted(struct sctp_outq *q, __u32 rtt; __u8 restart_timer = 0; int bytes_acked = 0; + int migrate_bytes = 0; /* These state variables are for coherent debug output. --xguo */ @@ -1343,8 +1336,9 @@ static void sctp_check_transmitted(struct sctp_outq *q, * considering it as 'outstanding'. */ if (!tchunk->tsn_gap_acked) { - tchunk->transport->flight_size -= - sctp_data_size(tchunk); + if (tchunk->transport) + tchunk->transport->flight_size -= + sctp_data_size(tchunk); q->outstanding_bytes -= sctp_data_size(tchunk); } continue; @@ -1378,6 +1372,20 @@ static void sctp_check_transmitted(struct sctp_outq *q, rtt); } } + + /* If the chunk hasn't been marked as ACKED, + * mark it and account bytes_acked if the + * chunk had a valid transport (it will not + * have a transport if ASCONF had deleted it + * while DATA was outstanding). + */ + if (!tchunk->tsn_gap_acked) { + tchunk->tsn_gap_acked = 1; + bytes_acked += sctp_data_size(tchunk); + if (!tchunk->transport) + migrate_bytes += sctp_data_size(tchunk); + } + if (TSN_lte(tsn, sack_ctsn)) { /* RFC 2960 6.3.2 Retransmission Timer Rules * @@ -1391,8 +1399,6 @@ static void sctp_check_transmitted(struct sctp_outq *q, restart_timer = 1; if (!tchunk->tsn_gap_acked) { - tchunk->tsn_gap_acked = 1; - bytes_acked += sctp_data_size(tchunk); /* * SFR-CACC algorithm: * 2) If the SACK contains gap acks @@ -1432,10 +1438,6 @@ static void sctp_check_transmitted(struct sctp_outq *q, * older than that newly acknowledged DATA * chunk, are qualified as 'Stray DATA chunks'. */ - if (!tchunk->tsn_gap_acked) { - tchunk->tsn_gap_acked = 1; - bytes_acked += sctp_data_size(tchunk); - } list_add_tail(lchunk, &tlist); } @@ -1491,7 +1493,8 @@ static void sctp_check_transmitted(struct sctp_outq *q, tsn); tchunk->tsn_gap_acked = 0; - bytes_acked -= sctp_data_size(tchunk); + if (tchunk->transport) + bytes_acked -= sctp_data_size(tchunk); /* RFC 2960 6.3.2 Retransmission Timer Rules * @@ -1561,6 +1564,14 @@ static void sctp_check_transmitted(struct sctp_outq *q, #endif /* SCTP_DEBUG */ if (transport) { if (bytes_acked) { + /* We may have counted DATA that was migrated + * to this transport due to DEL-IP operation. + * Subtract those bytes, since the were never + * send on this transport and shouldn't be + * credited to this transport. + */ + bytes_acked -= migrate_bytes; + /* 8.2. When an outstanding TSN is acknowledged, * the endpoint shall clear the error counter of * the destination transport address to which the @@ -1589,7 +1600,7 @@ static void sctp_check_transmitted(struct sctp_outq *q, transport->flight_size -= bytes_acked; if (transport->flight_size == 0) transport->partial_bytes_acked = 0; - q->outstanding_bytes -= bytes_acked; + q->outstanding_bytes -= bytes_acked + migrate_bytes; } else { /* RFC 2960 6.1, sctpimpguide-06 2.15.2 * When a sender is doing zero window probing, it diff --git a/net/sctp/proc.c b/net/sctp/proc.c index f268910620b..d093cbfeaac 100644 --- a/net/sctp/proc.c +++ b/net/sctp/proc.c @@ -512,10 +512,8 @@ int __init sctp_remaddr_proc_init(void) { struct proc_dir_entry *p; - p = create_proc_entry("remaddr", S_IRUGO, proc_net_sctp); + p = proc_create("remaddr", S_IRUGO, proc_net_sctp, &sctp_remaddr_seq_fops); if (!p) return -ENOMEM; - p->proc_fops = &sctp_remaddr_seq_fops; - return 0; } diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index 79cbd47f4df..612dc878e05 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -160,6 +160,7 @@ static void sctp_proc_exit(void) remove_proc_entry("sctp", init_net.proc_net); } #endif + percpu_counter_destroy(&sctp_sockets_allocated); } /* Private helper to extract ipv4 address and stash them in @@ -430,16 +431,14 @@ static int sctp_v4_available(union sctp_addr *addr, struct sctp_sock *sp) * of requested destination address, sender and receiver * SHOULD include all of its addresses with level greater * than or equal to L. + * + * IPv4 scoping can be controlled through sysctl option + * net.sctp.addr_scope_policy */ static sctp_scope_t sctp_v4_scope(union sctp_addr *addr) { sctp_scope_t retval; - /* Should IPv4 scoping be a sysctl configurable option - * so users can turn it off (default on) for certain - * unconventional networking environments? - */ - /* Check for unusable SCTP addresses. */ if (IS_IPV4_UNUSABLE_ADDRESS(addr->v4.sin_addr.s_addr)) { retval = SCTP_SCOPE_UNUSABLE; @@ -925,7 +924,7 @@ static struct inet_protosw sctp_stream_protosw = { }; /* Register with IP layer. */ -static struct net_protocol sctp_protocol = { +static const struct net_protocol sctp_protocol = { .handler = sctp_rcv, .err_handler = sctp_v4_err, .no_policy = 1, @@ -1185,10 +1184,10 @@ SCTP_STATIC __init int sctp_init(void) /* Size and allocate the association hash table. * The methodology is similar to that of the tcp hash tables. */ - if (num_physpages >= (128 * 1024)) - goal = num_physpages >> (22 - PAGE_SHIFT); + if (totalram_pages >= (128 * 1024)) + goal = totalram_pages >> (22 - PAGE_SHIFT); else - goal = num_physpages >> (24 - PAGE_SHIFT); + goal = totalram_pages >> (24 - PAGE_SHIFT); for (order = 0; (1UL << order) < goal; order++) ; @@ -1258,6 +1257,9 @@ SCTP_STATIC __init int sctp_init(void) /* Disable AUTH by default. */ sctp_auth_enable = 0; + /* Set SCOPE policy to enabled */ + sctp_scope_policy = SCTP_SCOPE_POLICY_ENABLE; + sctp_sysctl_register(); INIT_LIST_HEAD(&sctp_address_families); diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index 61cc6075b0d..9d881a61ac0 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -2861,6 +2861,11 @@ static __be16 sctp_process_asconf_param(struct sctp_association *asoc, addr_param = (union sctp_addr_param *) ((void *)asconf_param + sizeof(sctp_addip_param_t)); + if (asconf_param->param_hdr.type != SCTP_PARAM_ADD_IP && + asconf_param->param_hdr.type != SCTP_PARAM_DEL_IP && + asconf_param->param_hdr.type != SCTP_PARAM_SET_PRIMARY) + return SCTP_ERROR_UNKNOWN_PARAM; + switch (addr_param->v4.param_hdr.type) { case SCTP_PARAM_IPV6_ADDRESS: if (!asoc->peer.ipv6_address) @@ -2958,9 +2963,6 @@ static __be16 sctp_process_asconf_param(struct sctp_association *asoc, sctp_assoc_set_primary(asoc, peer); break; - default: - return SCTP_ERROR_UNKNOWN_PARAM; - break; } return SCTP_ERROR_NO_ERROR; @@ -3104,7 +3106,7 @@ done: } /* Process a asconf parameter that is successfully acked. */ -static int sctp_asconf_param_success(struct sctp_association *asoc, +static void sctp_asconf_param_success(struct sctp_association *asoc, sctp_addip_param_t *asconf_param) { struct sctp_af *af; @@ -3113,7 +3115,6 @@ static int sctp_asconf_param_success(struct sctp_association *asoc, union sctp_addr_param *addr_param; struct sctp_transport *transport; struct sctp_sockaddr_entry *saddr; - int retval = 0; addr_param = (union sctp_addr_param *) ((void *)asconf_param + sizeof(sctp_addip_param_t)); @@ -3133,10 +3134,18 @@ static int sctp_asconf_param_success(struct sctp_association *asoc, saddr->state = SCTP_ADDR_SRC; } local_bh_enable(); + list_for_each_entry(transport, &asoc->peer.transport_addr_list, + transports) { + if (transport->state == SCTP_ACTIVE) + continue; + dst_release(transport->dst); + sctp_transport_route(transport, NULL, + sctp_sk(asoc->base.sk)); + } break; case SCTP_PARAM_DEL_IP: local_bh_disable(); - retval = sctp_del_bind_addr(bp, &addr); + sctp_del_bind_addr(bp, &addr); local_bh_enable(); list_for_each_entry(transport, &asoc->peer.transport_addr_list, transports) { @@ -3148,8 +3157,6 @@ static int sctp_asconf_param_success(struct sctp_association *asoc, default: break; } - - return retval; } /* Get the corresponding ASCONF response error code from the ASCONF_ACK chunk @@ -3266,7 +3273,7 @@ int sctp_process_asconf_ack(struct sctp_association *asoc, switch (err_code) { case SCTP_ERROR_NO_ERROR: - retval = sctp_asconf_param_success(asoc, asconf_param); + sctp_asconf_param_success(asoc, asconf_param); break; case SCTP_ERROR_RSRC_LOW: diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c index 86426aac160..efa516b47e8 100644 --- a/net/sctp/sm_sideeffect.c +++ b/net/sctp/sm_sideeffect.c @@ -440,14 +440,26 @@ static void sctp_do_8_2_transport_strike(struct sctp_association *asoc, /* The check for association's overall error counter exceeding the * threshold is done in the state function. */ - /* When probing UNCONFIRMED addresses, the association overall - * error count is NOT incremented + /* We are here due to a timer expiration. If the timer was + * not a HEARTBEAT, then normal error tracking is done. + * If the timer was a heartbeat, we only increment error counts + * when we already have an outstanding HEARTBEAT that has not + * been acknowledged. + * Additionaly, some tranport states inhibit error increments. */ - if (transport->state != SCTP_UNCONFIRMED) + if (!is_hb) { asoc->overall_error_count++; + if (transport->state != SCTP_INACTIVE) + transport->error_count++; + } else if (transport->hb_sent) { + if (transport->state != SCTP_UNCONFIRMED) + asoc->overall_error_count++; + if (transport->state != SCTP_INACTIVE) + transport->error_count++; + } if (transport->state != SCTP_INACTIVE && - (transport->error_count++ >= transport->pathmaxrxt)) { + (transport->error_count > transport->pathmaxrxt)) { SCTP_DEBUG_PRINTK_IPADDR("transport_strike:association %p", " transport IP: port:%d failed.\n", asoc, @@ -468,7 +480,6 @@ static void sctp_do_8_2_transport_strike(struct sctp_association *asoc, * that indicates that we have an outstanding HB. */ if (!is_hb || transport->hb_sent) { - transport->last_rto = transport->rto; transport->rto = min((transport->rto * 2), transport->asoc->rto_max); } } @@ -931,6 +942,27 @@ static void sctp_cmd_t1_timer_update(struct sctp_association *asoc, } +/* Send the whole message, chunk by chunk, to the outqueue. + * This way the whole message is queued up and bundling if + * encouraged for small fragments. + */ +static int sctp_cmd_send_msg(struct sctp_association *asoc, + struct sctp_datamsg *msg) +{ + struct sctp_chunk *chunk; + int error = 0; + + list_for_each_entry(chunk, &msg->chunks, frag_list) { + error = sctp_outq_tail(&asoc->outqueue, chunk); + if (error) + break; + } + + return error; +} + + + /* These three macros allow us to pull the debugging code out of the * main flow of sctp_do_sm() to keep attention focused on the real * functionality there. @@ -1500,7 +1532,8 @@ static int sctp_cmd_interpreter(sctp_event_t event_type, case SCTP_CMD_PROCESS_CTSN: /* Dummy up a SACK for processing. */ sackh.cum_tsn_ack = cmd->obj.be32; - sackh.a_rwnd = 0; + sackh.a_rwnd = asoc->peer.rwnd + + asoc->outqueue.outstanding_bytes; sackh.num_gap_ack_blocks = 0; sackh.num_dup_tsns = 0; sctp_add_cmd_sf(commands, SCTP_CMD_PROCESS_SACK, @@ -1575,7 +1608,13 @@ static int sctp_cmd_interpreter(sctp_event_t event_type, case SCTP_CMD_UPDATE_INITTAG: asoc->peer.i.init_tag = cmd->obj.u32; break; - + case SCTP_CMD_SEND_MSG: + if (!asoc->outqueue.cork) { + sctp_outq_cork(&asoc->outqueue); + local_cork = 1; + } + error = sctp_cmd_send_msg(asoc, cmd->obj.msg); + break; default: printk(KERN_WARNING "Impossible command: %u, %p\n", cmd->verb, cmd->obj.ptr); @@ -1593,9 +1632,9 @@ out: */ if (asoc && SCTP_EVENT_T_CHUNK == event_type && chunk) { if (chunk->end_of_packet || chunk->singleton) - sctp_outq_uncork(&asoc->outqueue); + error = sctp_outq_uncork(&asoc->outqueue); } else if (local_cork) - sctp_outq_uncork(&asoc->outqueue); + error = sctp_outq_uncork(&asoc->outqueue); return error; nomem: error = -ENOMEM; diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 7288192f7df..d4df45022ff 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -334,6 +334,15 @@ sctp_disposition_t sctp_sf_do_5_1B_init(const struct sctp_endpoint *ep, if (!sctp_chunk_length_valid(chunk, sizeof(sctp_init_chunk_t))) return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + /* If the INIT is coming toward a closing socket, we'll send back + * and ABORT. Essentially, this catches the race of INIT being + * backloged to the socket at the same time as the user isses close(). + * Since the socket and all its associations are going away, we + * can treat this OOTB + */ + if (sctp_sstate(ep->base.sk, CLOSING)) + return sctp_sf_tabort_8_4_8(ep, asoc, type, arg, commands); + /* Verify the INIT chunk before processing it. */ err_chunk = NULL; if (!sctp_verify_init(asoc, chunk->chunk_hdr->type, @@ -375,6 +384,11 @@ sctp_disposition_t sctp_sf_do_5_1B_init(const struct sctp_endpoint *ep, if (!new_asoc) goto nomem; + if (sctp_assoc_set_bind_addr_from_ep(new_asoc, + sctp_scope(sctp_source(chunk)), + GFP_ATOMIC) < 0) + goto nomem_init; + /* The call, sctp_process_init(), can fail on memory allocation. */ if (!sctp_process_init(new_asoc, chunk->chunk_hdr->type, sctp_source(chunk), @@ -392,9 +406,6 @@ sctp_disposition_t sctp_sf_do_5_1B_init(const struct sctp_endpoint *ep, len = ntohs(err_chunk->chunk_hdr->length) - sizeof(sctp_chunkhdr_t); - if (sctp_assoc_set_bind_addr_from_ep(new_asoc, GFP_ATOMIC) < 0) - goto nomem_init; - repl = sctp_make_init_ack(new_asoc, chunk, GFP_ATOMIC, len); if (!repl) goto nomem_init; @@ -962,7 +973,7 @@ sctp_disposition_t sctp_sf_sendbeat_8_3(const struct sctp_endpoint *ep, { struct sctp_transport *transport = (struct sctp_transport *) arg; - if (asoc->overall_error_count > asoc->max_retrans) { + if (asoc->overall_error_count >= asoc->max_retrans) { sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR, SCTP_ERROR(ETIMEDOUT)); /* CMD_ASSOC_FAILED calls CMD_DELETE_TCB. */ @@ -1106,7 +1117,8 @@ sctp_disposition_t sctp_sf_backbeat_8_3(const struct sctp_endpoint *ep, return sctp_sf_pdiscard(ep, asoc, type, arg, commands); /* Make sure that the HEARTBEAT-ACK chunk has a valid length. */ - if (!sctp_chunk_length_valid(chunk, sizeof(sctp_heartbeat_chunk_t))) + if (!sctp_chunk_length_valid(chunk, sizeof(sctp_chunkhdr_t) + + sizeof(sctp_sender_hb_info_t))) return sctp_sf_violation_chunklen(ep, asoc, type, arg, commands); @@ -1442,6 +1454,10 @@ static sctp_disposition_t sctp_sf_do_unexpected_init( if (!new_asoc) goto nomem; + if (sctp_assoc_set_bind_addr_from_ep(new_asoc, + sctp_scope(sctp_source(chunk)), GFP_ATOMIC) < 0) + goto nomem; + /* In the outbound INIT ACK the endpoint MUST copy its current * Verification Tag and Peers Verification tag into a reserved * place (local tie-tag and per tie-tag) within the state cookie. @@ -1478,9 +1494,6 @@ static sctp_disposition_t sctp_sf_do_unexpected_init( sizeof(sctp_chunkhdr_t); } - if (sctp_assoc_set_bind_addr_from_ep(new_asoc, GFP_ATOMIC) < 0) - goto nomem; - repl = sctp_make_init_ack(new_asoc, chunk, GFP_ATOMIC, len); if (!repl) goto nomem; @@ -2561,6 +2574,12 @@ sctp_disposition_t sctp_sf_do_9_2_shutdown(const struct sctp_endpoint *ep, chunk->subh.shutdown_hdr = sdh; ctsn = ntohl(sdh->cum_tsn_ack); + if (TSN_lt(ctsn, asoc->ctsn_ack_point)) { + SCTP_DEBUG_PRINTK("ctsn %x\n", ctsn); + SCTP_DEBUG_PRINTK("ctsn_ack_point %x\n", asoc->ctsn_ack_point); + return SCTP_DISPOSITION_DISCARD; + } + /* If Cumulative TSN Ack beyond the max tsn currently * send, terminating the association and respond to the * sender with an ABORT. @@ -2624,6 +2643,7 @@ sctp_disposition_t sctp_sf_do_9_2_shut_ctsn(const struct sctp_endpoint *ep, { struct sctp_chunk *chunk = arg; sctp_shutdownhdr_t *sdh; + __u32 ctsn; if (!sctp_vtag_verify(chunk, asoc)) return sctp_sf_pdiscard(ep, asoc, type, arg, commands); @@ -2635,12 +2655,19 @@ sctp_disposition_t sctp_sf_do_9_2_shut_ctsn(const struct sctp_endpoint *ep, commands); sdh = (sctp_shutdownhdr_t *)chunk->skb->data; + ctsn = ntohl(sdh->cum_tsn_ack); + + if (TSN_lt(ctsn, asoc->ctsn_ack_point)) { + SCTP_DEBUG_PRINTK("ctsn %x\n", ctsn); + SCTP_DEBUG_PRINTK("ctsn_ack_point %x\n", asoc->ctsn_ack_point); + return SCTP_DISPOSITION_DISCARD; + } /* If Cumulative TSN Ack beyond the max tsn currently * send, terminating the association and respond to the * sender with an ABORT. */ - if (!TSN_lt(ntohl(sdh->cum_tsn_ack), asoc->next_tsn)) + if (!TSN_lt(ctsn, asoc->next_tsn)) return sctp_sf_violation_ctsn(ep, asoc, type, arg, commands); /* verify, by checking the Cumulative TSN Ack field of the @@ -2867,6 +2894,9 @@ sctp_disposition_t sctp_sf_eat_data_6_2(const struct sctp_endpoint *ep, goto discard_force; case SCTP_IERROR_NO_DATA: goto consume; + case SCTP_IERROR_PROTO_VIOLATION: + return sctp_sf_abort_violation(ep, asoc, chunk, commands, + (u8 *)chunk->subh.data_hdr, sizeof(sctp_datahdr_t)); default: BUG(); } @@ -2977,6 +3007,9 @@ sctp_disposition_t sctp_sf_eat_data_fast_4_4(const struct sctp_endpoint *ep, break; case SCTP_IERROR_NO_DATA: goto consume; + case SCTP_IERROR_PROTO_VIOLATION: + return sctp_sf_abort_violation(ep, asoc, chunk, commands, + (u8 *)chunk->subh.data_hdr, sizeof(sctp_datahdr_t)); default: BUG(); } @@ -3519,6 +3552,12 @@ sctp_disposition_t sctp_sf_do_asconf(const struct sctp_endpoint *ep, asconf_ack = sctp_assoc_lookup_asconf_ack(asoc, hdr->serial); if (!asconf_ack) return SCTP_DISPOSITION_DISCARD; + + /* Reset the transport so that we select the correct one + * this time around. This is to make sure that we don't + * accidentally use a stale transport that's been removed. + */ + asconf_ack->transport = NULL; } else { /* ADDIP 5.2 E5) Otherwise, the ASCONF Chunk is discarded since * it must be either a stale packet or from an attacker. @@ -4546,9 +4585,9 @@ sctp_disposition_t sctp_sf_do_prm_send(const struct sctp_endpoint *ep, void *arg, sctp_cmd_seq_t *commands) { - struct sctp_chunk *chunk = arg; + struct sctp_datamsg *msg = arg; - sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(chunk)); + sctp_add_cmd_sf(commands, SCTP_CMD_SEND_MSG, SCTP_DATAMSG(msg)); return SCTP_DISPOSITION_CONSUME; } @@ -5847,6 +5886,9 @@ static int sctp_eat_data(const struct sctp_association *asoc, __u32 tsn; struct sctp_tsnmap *map = (struct sctp_tsnmap *)&asoc->peer.tsn_map; struct sock *sk = asoc->base.sk; + u16 ssn; + u16 sid; + u8 ordered = 0; data_hdr = chunk->subh.data_hdr = (sctp_datahdr_t *)chunk->skb->data; skb_pull(chunk->skb, sizeof(sctp_datahdr_t)); @@ -5986,8 +6028,10 @@ static int sctp_eat_data(const struct sctp_association *asoc, */ if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED) SCTP_INC_STATS(SCTP_MIB_INUNORDERCHUNKS); - else + else { SCTP_INC_STATS(SCTP_MIB_INORDERCHUNKS); + ordered = 1; + } /* RFC 2960 6.5 Stream Identifier and Stream Sequence Number * @@ -5997,7 +6041,8 @@ static int sctp_eat_data(const struct sctp_association *asoc, * with cause set to "Invalid Stream Identifier" (See Section 3.3.10) * and discard the DATA chunk. */ - if (ntohs(data_hdr->stream) >= asoc->c.sinit_max_instreams) { + sid = ntohs(data_hdr->stream); + if (sid >= asoc->c.sinit_max_instreams) { /* Mark tsn as received even though we drop it */ sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_TSN, SCTP_U32(tsn)); @@ -6010,6 +6055,18 @@ static int sctp_eat_data(const struct sctp_association *asoc, return SCTP_IERROR_BAD_STREAM; } + /* Check to see if the SSN is possible for this TSN. + * The biggest gap we can record is 4K wide. Since SSNs wrap + * at an unsigned short, there is no way that an SSN can + * wrap and for a valid TSN. We can simply check if the current + * SSN is smaller then the next expected one. If it is, it wrapped + * and is invalid. + */ + ssn = ntohs(data_hdr->ssn); + if (ordered && SSN_lt(ssn, sctp_ssn_peek(&asoc->ssnmap->in, sid))) { + return SCTP_IERROR_PROTO_VIOLATION; + } + /* Send the data up to the user. Note: Schedule the * SCTP_CMD_CHUNK_ULP cmd before the SCTP_CMD_GEN_SACK, as the SACK * chunk needs the updated rwnd. diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 35ba035970a..3a95fcb17a9 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -1080,6 +1080,13 @@ static int __sctp_connect(struct sock* sk, err = -ENOMEM; goto out_free; } + + err = sctp_assoc_set_bind_addr_from_ep(asoc, scope, + GFP_KERNEL); + if (err < 0) { + goto out_free; + } + } /* Prime the peer's transport structures. */ @@ -1095,11 +1102,6 @@ static int __sctp_connect(struct sock* sk, walk_size += af->sockaddr_len; } - err = sctp_assoc_set_bind_addr_from_ep(asoc, GFP_KERNEL); - if (err < 0) { - goto out_free; - } - /* In case the user of sctp_connectx() wants an association * id back, assign one now. */ @@ -1274,22 +1276,30 @@ SCTP_STATIC int sctp_setsockopt_connectx(struct sock* sk, } /* - * New (hopefully final) interface for the API. The option buffer is used - * both for the returned association id and the addresses. + * New (hopefully final) interface for the API. + * We use the sctp_getaddrs_old structure so that use-space library + * can avoid any unnecessary allocations. The only defferent part + * is that we store the actual length of the address buffer into the + * addrs_num structure member. That way we can re-use the existing + * code. */ SCTP_STATIC int sctp_getsockopt_connectx3(struct sock* sk, int len, char __user *optval, int __user *optlen) { + struct sctp_getaddrs_old param; sctp_assoc_t assoc_id = 0; int err = 0; - if (len < sizeof(assoc_id)) + if (len < sizeof(param)) return -EINVAL; + if (copy_from_user(¶m, optval, sizeof(param))) + return -EFAULT; + err = __sctp_setsockopt_connectx(sk, - (struct sockaddr __user *)(optval + sizeof(assoc_id)), - len - sizeof(assoc_id), &assoc_id); + (struct sockaddr __user *)param.addrs, + param.addr_num, &assoc_id); if (err == 0 || err == -EINPROGRESS) { if (copy_to_user(optval, &assoc_id, sizeof(assoc_id))) @@ -1361,6 +1371,7 @@ SCTP_STATIC void sctp_close(struct sock *sk, long timeout) sctp_lock_sock(sk); sk->sk_shutdown = SHUTDOWN_MASK; + sk->sk_state = SCTP_SS_CLOSING; ep = sctp_sk(sk)->ep; @@ -1688,6 +1699,11 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, goto out_unlock; } asoc = new_asoc; + err = sctp_assoc_set_bind_addr_from_ep(asoc, scope, GFP_KERNEL); + if (err < 0) { + err = -ENOMEM; + goto out_free; + } /* If the SCTP_INIT ancillary data is specified, set all * the association init values accordingly. @@ -1717,11 +1733,6 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, err = -ENOMEM; goto out_free; } - err = sctp_assoc_set_bind_addr_from_ep(asoc, GFP_KERNEL); - if (err < 0) { - err = -ENOMEM; - goto out_free; - } } /* ASSERT: we have a valid association at this point. */ @@ -1813,20 +1824,22 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, sctp_set_owner_w(chunk); chunk->transport = chunk_tp; - - /* Send it to the lower layers. Note: all chunks - * must either fail or succeed. The lower layer - * works that way today. Keep it that way or this - * breaks. - */ - err = sctp_primitive_SEND(asoc, chunk); - /* Did the lower layer accept the chunk? */ - if (err) - sctp_chunk_free(chunk); - SCTP_DEBUG_PRINTK("We sent primitively.\n"); } - sctp_datamsg_put(datamsg); + /* Send it to the lower layers. Note: all chunks + * must either fail or succeed. The lower layer + * works that way today. Keep it that way or this + * breaks. + */ + err = sctp_primitive_SEND(asoc, datamsg); + /* Did the lower layer accept the chunk? */ + if (err) + sctp_datamsg_free(datamsg); + else + sctp_datamsg_put(datamsg); + + SCTP_DEBUG_PRINTK("We sent primitively.\n"); + if (err) goto out_free; else @@ -2024,7 +2037,8 @@ out: * instead a error will be indicated to the user. */ static int sctp_setsockopt_disable_fragments(struct sock *sk, - char __user *optval, int optlen) + char __user *optval, + unsigned int optlen) { int val; @@ -2040,7 +2054,7 @@ static int sctp_setsockopt_disable_fragments(struct sock *sk, } static int sctp_setsockopt_events(struct sock *sk, char __user *optval, - int optlen) + unsigned int optlen) { if (optlen > sizeof(struct sctp_event_subscribe)) return -EINVAL; @@ -2061,7 +2075,7 @@ static int sctp_setsockopt_events(struct sock *sk, char __user *optval, * association is closed. */ static int sctp_setsockopt_autoclose(struct sock *sk, char __user *optval, - int optlen) + unsigned int optlen) { struct sctp_sock *sp = sctp_sk(sk); @@ -2240,7 +2254,7 @@ static int sctp_apply_peer_addr_params(struct sctp_paddrparams *params, sctp_assoc_sync_pmtu(asoc); } else if (asoc) { asoc->pathmtu = params->spp_pathmtu; - sctp_frag_point(sp, params->spp_pathmtu); + sctp_frag_point(asoc, params->spp_pathmtu); } else { sp->pathmtu = params->spp_pathmtu; } @@ -2315,7 +2329,8 @@ static int sctp_apply_peer_addr_params(struct sctp_paddrparams *params, } static int sctp_setsockopt_peer_addr_params(struct sock *sk, - char __user *optval, int optlen) + char __user *optval, + unsigned int optlen) { struct sctp_paddrparams params; struct sctp_transport *trans = NULL; @@ -2427,7 +2442,7 @@ static int sctp_setsockopt_peer_addr_params(struct sock *sk, */ static int sctp_setsockopt_delayed_ack(struct sock *sk, - char __user *optval, int optlen) + char __user *optval, unsigned int optlen) { struct sctp_sack_info params; struct sctp_transport *trans = NULL; @@ -2543,7 +2558,7 @@ static int sctp_setsockopt_delayed_ack(struct sock *sk, * by the change). With TCP-style sockets, this option is inherited by * sockets derived from a listener socket. */ -static int sctp_setsockopt_initmsg(struct sock *sk, char __user *optval, int optlen) +static int sctp_setsockopt_initmsg(struct sock *sk, char __user *optval, unsigned int optlen) { struct sctp_initmsg sinit; struct sctp_sock *sp = sctp_sk(sk); @@ -2580,7 +2595,8 @@ static int sctp_setsockopt_initmsg(struct sock *sk, char __user *optval, int opt * to this call if the caller is using the UDP model. */ static int sctp_setsockopt_default_send_param(struct sock *sk, - char __user *optval, int optlen) + char __user *optval, + unsigned int optlen) { struct sctp_sndrcvinfo info; struct sctp_association *asoc; @@ -2619,7 +2635,7 @@ static int sctp_setsockopt_default_send_param(struct sock *sk, * association peer's addresses. */ static int sctp_setsockopt_primary_addr(struct sock *sk, char __user *optval, - int optlen) + unsigned int optlen) { struct sctp_prim prim; struct sctp_transport *trans; @@ -2648,7 +2664,7 @@ static int sctp_setsockopt_primary_addr(struct sock *sk, char __user *optval, * integer boolean flag. */ static int sctp_setsockopt_nodelay(struct sock *sk, char __user *optval, - int optlen) + unsigned int optlen) { int val; @@ -2673,7 +2689,8 @@ static int sctp_setsockopt_nodelay(struct sock *sk, char __user *optval, * be changed. * */ -static int sctp_setsockopt_rtoinfo(struct sock *sk, char __user *optval, int optlen) { +static int sctp_setsockopt_rtoinfo(struct sock *sk, char __user *optval, unsigned int optlen) +{ struct sctp_rtoinfo rtoinfo; struct sctp_association *asoc; @@ -2725,7 +2742,7 @@ static int sctp_setsockopt_rtoinfo(struct sock *sk, char __user *optval, int opt * See [SCTP] for more information. * */ -static int sctp_setsockopt_associnfo(struct sock *sk, char __user *optval, int optlen) +static int sctp_setsockopt_associnfo(struct sock *sk, char __user *optval, unsigned int optlen) { struct sctp_assocparams assocparams; @@ -2797,7 +2814,7 @@ static int sctp_setsockopt_associnfo(struct sock *sk, char __user *optval, int o * addresses and a user will receive both PF_INET6 and PF_INET type * addresses on the socket. */ -static int sctp_setsockopt_mappedv4(struct sock *sk, char __user *optval, int optlen) +static int sctp_setsockopt_mappedv4(struct sock *sk, char __user *optval, unsigned int optlen) { int val; struct sctp_sock *sp = sctp_sk(sk); @@ -2841,7 +2858,7 @@ static int sctp_setsockopt_mappedv4(struct sock *sk, char __user *optval, int op * changed (effecting future associations only). * assoc_value: This parameter specifies the maximum size in bytes. */ -static int sctp_setsockopt_maxseg(struct sock *sk, char __user *optval, int optlen) +static int sctp_setsockopt_maxseg(struct sock *sk, char __user *optval, unsigned int optlen) { struct sctp_assoc_value params; struct sctp_association *asoc; @@ -2877,15 +2894,10 @@ static int sctp_setsockopt_maxseg(struct sock *sk, char __user *optval, int optl val -= sizeof(struct sctphdr) + sizeof(struct sctp_data_chunk); } - - asoc->frag_point = val; + asoc->user_frag = val; + asoc->frag_point = sctp_frag_point(asoc, asoc->pathmtu); } else { sp->user_frag = val; - - /* Update the frag_point of the existing associations. */ - list_for_each_entry(asoc, &(sp->ep->asocs), asocs) { - asoc->frag_point = sctp_frag_point(sp, asoc->pathmtu); - } } return 0; @@ -2901,7 +2913,7 @@ static int sctp_setsockopt_maxseg(struct sock *sk, char __user *optval, int optl * set primary request: */ static int sctp_setsockopt_peer_primary_addr(struct sock *sk, char __user *optval, - int optlen) + unsigned int optlen) { struct sctp_sock *sp; struct sctp_endpoint *ep; @@ -2952,7 +2964,7 @@ static int sctp_setsockopt_peer_primary_addr(struct sock *sk, char __user *optva } static int sctp_setsockopt_adaptation_layer(struct sock *sk, char __user *optval, - int optlen) + unsigned int optlen) { struct sctp_setadaptation adaptation; @@ -2981,7 +2993,7 @@ static int sctp_setsockopt_adaptation_layer(struct sock *sk, char __user *optval * saved with outbound messages. */ static int sctp_setsockopt_context(struct sock *sk, char __user *optval, - int optlen) + unsigned int optlen) { struct sctp_assoc_value params; struct sctp_sock *sp; @@ -3032,7 +3044,7 @@ static int sctp_setsockopt_context(struct sock *sk, char __user *optval, */ static int sctp_setsockopt_fragment_interleave(struct sock *sk, char __user *optval, - int optlen) + unsigned int optlen) { int val; @@ -3065,7 +3077,7 @@ static int sctp_setsockopt_fragment_interleave(struct sock *sk, */ static int sctp_setsockopt_partial_delivery_point(struct sock *sk, char __user *optval, - int optlen) + unsigned int optlen) { u32 val; @@ -3098,7 +3110,7 @@ static int sctp_setsockopt_partial_delivery_point(struct sock *sk, */ static int sctp_setsockopt_maxburst(struct sock *sk, char __user *optval, - int optlen) + unsigned int optlen) { struct sctp_assoc_value params; struct sctp_sock *sp; @@ -3142,8 +3154,8 @@ static int sctp_setsockopt_maxburst(struct sock *sk, * will only effect future associations on the socket. */ static int sctp_setsockopt_auth_chunk(struct sock *sk, - char __user *optval, - int optlen) + char __user *optval, + unsigned int optlen) { struct sctp_authchunk val; @@ -3174,8 +3186,8 @@ static int sctp_setsockopt_auth_chunk(struct sock *sk, * endpoint requires the peer to use. */ static int sctp_setsockopt_hmac_ident(struct sock *sk, - char __user *optval, - int optlen) + char __user *optval, + unsigned int optlen) { struct sctp_hmacalgo *hmacs; u32 idents; @@ -3217,7 +3229,7 @@ out: */ static int sctp_setsockopt_auth_key(struct sock *sk, char __user *optval, - int optlen) + unsigned int optlen) { struct sctp_authkey *authkey; struct sctp_association *asoc; @@ -3262,8 +3274,8 @@ out: * the association shared key. */ static int sctp_setsockopt_active_key(struct sock *sk, - char __user *optval, - int optlen) + char __user *optval, + unsigned int optlen) { struct sctp_authkeyid val; struct sctp_association *asoc; @@ -3290,8 +3302,8 @@ static int sctp_setsockopt_active_key(struct sock *sk, * This set option will delete a shared secret key from use. */ static int sctp_setsockopt_del_key(struct sock *sk, - char __user *optval, - int optlen) + char __user *optval, + unsigned int optlen) { struct sctp_authkeyid val; struct sctp_association *asoc; @@ -3334,7 +3346,7 @@ static int sctp_setsockopt_del_key(struct sock *sk, * optlen - the size of the buffer. */ SCTP_STATIC int sctp_setsockopt(struct sock *sk, int level, int optname, - char __user *optval, int optlen) + char __user *optval, unsigned int optlen) { int retval = 0; @@ -6652,21 +6664,6 @@ static void sctp_wait_for_close(struct sock *sk, long timeout) finish_wait(sk->sk_sleep, &wait); } -static void sctp_sock_rfree_frag(struct sk_buff *skb) -{ - struct sk_buff *frag; - - if (!skb->data_len) - goto done; - - /* Don't forget the fragments. */ - skb_walk_frags(skb, frag) - sctp_sock_rfree_frag(frag); - -done: - sctp_sock_rfree(skb); -} - static void sctp_skb_set_owner_r_frag(struct sk_buff *skb, struct sock *sk) { struct sk_buff *frag; @@ -6776,7 +6773,6 @@ static void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk, sctp_skb_for_each(skb, &oldsk->sk_receive_queue, tmp) { event = sctp_skb2event(skb); if (event->asoc == assoc) { - sctp_sock_rfree_frag(skb); __skb_unlink(skb, &oldsk->sk_receive_queue); __skb_queue_tail(&newsk->sk_receive_queue, skb); sctp_skb_set_owner_r_frag(skb, newsk); @@ -6807,7 +6803,6 @@ static void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk, sctp_skb_for_each(skb, &oldsp->pd_lobby, tmp) { event = sctp_skb2event(skb); if (event->asoc == assoc) { - sctp_sock_rfree_frag(skb); __skb_unlink(skb, &oldsp->pd_lobby); __skb_queue_tail(queue, skb); sctp_skb_set_owner_r_frag(skb, newsk); @@ -6822,15 +6817,11 @@ static void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk, } - sctp_skb_for_each(skb, &assoc->ulpq.reasm, tmp) { - sctp_sock_rfree_frag(skb); + sctp_skb_for_each(skb, &assoc->ulpq.reasm, tmp) sctp_skb_set_owner_r_frag(skb, newsk); - } - sctp_skb_for_each(skb, &assoc->ulpq.lobby, tmp) { - sctp_sock_rfree_frag(skb); + sctp_skb_for_each(skb, &assoc->ulpq.lobby, tmp) sctp_skb_set_owner_r_frag(skb, newsk); - } /* Set the type of socket to indicate that it is peeled off from the * original UDP-style socket or created with the accept() call on a diff --git a/net/sctp/sysctl.c b/net/sctp/sysctl.c index 63eabbc7129..ab7151da120 100644 --- a/net/sctp/sysctl.c +++ b/net/sctp/sysctl.c @@ -51,6 +51,7 @@ static int timer_max = 86400000; /* ms in one day */ static int int_max = INT_MAX; static int sack_timer_min = 1; static int sack_timer_max = 500; +static int addr_scope_max = 3; /* check sctp_scope_policy_t in include/net/sctp/constants.h for max entries */ extern int sysctl_sctp_mem[3]; extern int sysctl_sctp_rmem[3]; @@ -272,6 +273,17 @@ static ctl_table sctp_table[] = { .proc_handler = proc_dointvec, .strategy = sysctl_intvec }, + { + .ctl_name = CTL_UNNUMBERED, + .procname = "addr_scope_policy", + .data = &sctp_scope_policy, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec_minmax, + .strategy = &sysctl_intvec, + .extra1 = &zero, + .extra2 = &addr_scope_max, + }, { .ctl_name = 0 } }; diff --git a/net/sctp/transport.c b/net/sctp/transport.c index e5dde45c79d..37a1184d789 100644 --- a/net/sctp/transport.c +++ b/net/sctp/transport.c @@ -74,7 +74,7 @@ static struct sctp_transport *sctp_transport_init(struct sctp_transport *peer, * given destination transport address, set RTO to the protocol * parameter 'RTO.Initial'. */ - peer->last_rto = peer->rto = msecs_to_jiffies(sctp_rto_initial); + peer->rto = msecs_to_jiffies(sctp_rto_initial); peer->rtt = 0; peer->rttvar = 0; peer->srtt = 0; @@ -308,7 +308,8 @@ void sctp_transport_route(struct sctp_transport *transport, /* Initialize sk->sk_rcv_saddr, if the transport is the * association's active path for getsockname(). */ - if (asoc && (transport == asoc->peer.active_path)) + if (asoc && (!asoc->peer.primary_path || + (transport == asoc->peer.active_path))) opt->pf->af->to_sk_saddr(&transport->saddr, asoc->base.sk); } else @@ -385,7 +386,6 @@ void sctp_transport_update_rto(struct sctp_transport *tp, __u32 rtt) tp->rto = tp->asoc->rto_max; tp->rtt = rtt; - tp->last_rto = tp->rto; /* Reset rto_pending so that a new RTT measurement is started when a * new data chunk is sent. @@ -503,6 +503,9 @@ void sctp_transport_lower_cwnd(struct sctp_transport *transport, transport->ssthresh = max(transport->cwnd/2, 4*transport->asoc->pathmtu); transport->cwnd = transport->asoc->pathmtu; + + /* T3-rtx also clears fast recovery on the transport */ + transport->fast_recovery = 0; break; case SCTP_LOWER_CWND_FAST_RTX: @@ -598,7 +601,7 @@ void sctp_transport_reset(struct sctp_transport *t) */ t->cwnd = min(4*asoc->pathmtu, max_t(__u32, 2*asoc->pathmtu, 4380)); t->ssthresh = asoc->peer.i.a_rwnd; - t->last_rto = t->rto = asoc->rto_initial; + t->rto = asoc->rto_initial; t->rtt = 0; t->srtt = 0; t->rttvar = 0; |