From dec5c9117b168c7e0a6a6a06ad40a485c401d81d Mon Sep 17 00:00:00 2001 From: Jon Grimm Date: Wed, 9 Oct 2002 00:25:56 -0500 Subject: sctp: Various invalid address check fixes. (jgrimm) 1) Need to check inside INIT chunks for addresses, not just INIT-ACK otherwise we don't recognize restart. 2) The address check logic isn't being excercised early enough in the restart. Per the impl-guide it needs to happen as early as INIT processing in COOKIE-ECHOED state. 3) Eliminate more uses of deprecated sctpParam_t --- include/net/sctp/sm.h | 4 ++-- include/net/sctp/structs.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'include/net') diff --git a/include/net/sctp/sm.h b/include/net/sctp/sm.h index 9459322a411b..fc9940ff2651 100644 --- a/include/net/sctp/sm.h +++ b/include/net/sctp/sm.h @@ -329,10 +329,10 @@ __u32 sctp_generate_tag(const sctp_endpoint_t *); __u32 sctp_generate_tsn(const sctp_endpoint_t *); /* 4th level prototypes */ -void sctp_param2sockaddr(sockaddr_storage_t *addr, const sctpParam_t param, +void sctp_param2sockaddr(sockaddr_storage_t *addr, sctp_addr_param_t *, __u16 port); int sctp_addr2sockaddr(const sctpParam_t, sockaddr_storage_t *); -int sockaddr2sctp_addr(const sockaddr_storage_t *, sctpParam_t); +int sockaddr2sctp_addr(const sockaddr_storage_t *, sctp_addr_param_t *); /* Extern declarations for major data structures. */ sctp_sm_table_entry_t *sctp_chunk_event_lookup(sctp_cid_t, sctp_state_t); diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index c3f11bdf3aeb..a102c5dff039 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -378,7 +378,7 @@ typedef union { typedef union { sctp_ipv4addr_param_t v4; sctp_ipv6addr_param_t v6; -} sctpIpAddress_t; +} sctp_addr_param_t; /* RFC 2960. Section 3.3.5 Heartbeat. * Heartbeat Information: variable length -- cgit v1.2.3 From ef012690c07245bc6b7fc6d126ee7d9d2f796ac9 Mon Sep 17 00:00:00 2001 From: Sridhar Samudrala Date: Thu, 10 Oct 2002 21:38:36 -0700 Subject: sctp: Fixes a couple of sctp_peeloff() issues. * data/notifications waiting in the parent socket's receive queue should be moved to the peeled-off socket's receive queue. * sendmsg() to a peer already on an association that has been peeled off should fail and not create a new association. --- include/net/sctp/structs.h | 3 ++ net/sctp/input.c | 40 ++++++++++++++++++++++---- net/sctp/socket.c | 71 +++++++++++++++++++++++++++++++++++++--------- 3 files changed, 96 insertions(+), 18 deletions(-) (limited to 'include/net') diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index d1748a73e6b0..9ad2cb194ec3 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -1044,6 +1044,9 @@ sctp_association_t *sctp_endpoint_lookup_assoc(const sctp_endpoint_t *ep, sctp_endpoint_t *sctp_endpoint_is_match(sctp_endpoint_t *, const sockaddr_storage_t *); +int sctp_has_association(const sockaddr_storage_t *laddr, + const sockaddr_storage_t *paddr); + int sctp_verify_init(const sctp_association_t *asoc, sctp_cid_t cid, sctp_init_chunk_t *peer_init, diff --git a/net/sctp/input.c b/net/sctp/input.c index b90444c080bd..7c054577fa00 100644 --- a/net/sctp/input.c +++ b/net/sctp/input.c @@ -522,9 +522,9 @@ void __sctp_unhash_established(sctp_association_t *asoc) } /* Look up an association. */ -sctp_association_t *__sctp_rcv_lookup_association(const sockaddr_storage_t *laddr, - const sockaddr_storage_t *paddr, - sctp_transport_t **transportp) +sctp_association_t *__sctp_lookup_association(const sockaddr_storage_t *laddr, + const sockaddr_storage_t *paddr, + sctp_transport_t **transportp) { sctp_hashbucket_t *head; sctp_endpoint_common_t *epb; @@ -557,6 +557,36 @@ hit: return asoc; } +/* Look up an association. BH-safe. */ +sctp_association_t *sctp_lookup_association(const sockaddr_storage_t *laddr, + const sockaddr_storage_t *paddr, + sctp_transport_t **transportp) +{ + sctp_association_t *asoc; + + sctp_local_bh_disable(); + asoc = __sctp_lookup_association(laddr, paddr, transportp); + sctp_local_bh_enable(); + + return asoc; +} + +/* Is there an association matching the given local and peer addresses? */ +int sctp_has_association(const sockaddr_storage_t *laddr, + const sockaddr_storage_t *paddr) +{ + sctp_association_t *asoc; + sctp_transport_t *transport; + + if (asoc = sctp_lookup_association(laddr, paddr, &transport)) { + sock_put(asoc->base.sk); + sctp_association_put(asoc); + return 1; + } + + return 0; +} + /* * SCTP Implementors Guide, 2.18 Handling of address * parameters within the INIT or INIT-ACK. @@ -628,7 +658,7 @@ static sctp_association_t *__sctp_rcv_initack_lookup(struct sk_buff *skb, sctp_param2sockaddr(paddr, parm, ntohs(sh->source)); - asoc = __sctp_rcv_lookup_association(laddr, paddr, transportp); + asoc = __sctp_lookup_association(laddr, paddr, transportp); if (asoc) return asoc; } @@ -644,7 +674,7 @@ sctp_association_t *__sctp_rcv_lookup(struct sk_buff *skb, { sctp_association_t *asoc; - asoc = __sctp_rcv_lookup_association(laddr, paddr, transportp); + asoc = __sctp_lookup_association(laddr, paddr, transportp); /* Further lookup for INIT-ACK packet. * SCTP Implementors Guide, 2.18 Handling of address diff --git a/net/sctp/socket.c b/net/sctp/socket.c index bcdffb131a30..b2d3ca856385 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -820,7 +820,32 @@ SCTP_STATIC int sctp_sendmsg(struct sock *sk, struct msghdr *msg, int size) /* If a msg_name has been specified, assume this is to be used. */ if (msg_name) { + /* Look for a matching association on the endpoint. */ asoc = sctp_endpoint_lookup_assoc(ep, &to, &transport); + if (!asoc) { + struct list_head *pos; + struct sockaddr_storage_list *addr; + sctp_bind_addr_t *bp = &ep->base.bind_addr; + + sctp_read_lock(&ep->base.addr_lock); + + /* If we could not find a matching association on + * the endpoint, make sure that there is no peeled- + * off association. + */ + list_for_each(pos, &bp->address_list) { + addr = list_entry(pos, + struct sockaddr_storage_list, + list); + if (sctp_has_association(&addr->a, &to)) { + err = -EINVAL; + sctp_read_unlock(&ep->base.addr_lock); + goto out_unlock; + } + } + + sctp_read_unlock(&ep->base.addr_lock); + } } else { /* For a peeled-off socket, ignore any associd specified by * the user with SNDRCVINFO. @@ -1583,6 +1608,8 @@ SCTP_STATIC int sctp_do_peeloff(sctp_association_t *assoc, struct socket **newso sctp_endpoint_t *newep; sctp_opt_t *oldsp = sctp_sk(oldsk); sctp_opt_t *newsp; + struct sk_buff *skb, *tmp; + sctp_ulpevent_t *event; int err = 0; /* An association cannot be branched off from an already peeled-off @@ -1612,6 +1639,17 @@ SCTP_STATIC int sctp_do_peeloff(sctp_association_t *assoc, struct socket **newso */ newsp->ep = newep; + /* Move any messages in the old socket's receive queue that are for the + * peeled off association to the new socket's receive queue. + */ + sctp_skb_for_each(skb, &oldsk->receive_queue, tmp) { + event = (sctp_ulpevent_t *)skb->cb; + if (event->asoc == assoc) { + __skb_unlink(skb, skb->list); + __skb_queue_tail(&newsk->receive_queue, skb); + } + } + /* Set the type of socket to indicate that it is peeled off from the * original socket. */ @@ -1629,39 +1667,46 @@ static inline int sctp_getsockopt_peeloff(struct sock *sk, int len, char *optval { sctp_peeloff_arg_t peeloff; struct socket *newsock; - int err, sd; + int retval = 0; sctp_association_t *assoc; if (len != sizeof(sctp_peeloff_arg_t)) return -EINVAL; if (copy_from_user(&peeloff, optval, len)) return -EFAULT; + + sctp_lock_sock(sk); + assoc = sctp_id2assoc(sk, peeloff.associd); - if (NULL == assoc) - return -EINVAL; + if (NULL == assoc) { + retval = -EINVAL; + goto out_unlock; + } SCTP_DEBUG_PRINTK("%s: sk: %p assoc: %p\n", __FUNCTION__, sk, assoc); - err = sctp_do_peeloff(assoc, &newsock); - if (err < 0) - return err; + retval = sctp_do_peeloff(assoc, &newsock); + if (retval < 0) + goto out_unlock; /* Map the socket to an unused fd that can be returned to the user. */ - sd = sock_map_fd(newsock); - if (sd < 0) { + retval = sock_map_fd(newsock); + if (retval < 0) { sock_release(newsock); - return sd; + goto out_unlock; } SCTP_DEBUG_PRINTK("%s: sk: %p assoc: %p newsk: %p sd: %d\n", - __FUNCTION__, sk, assoc, newsock->sk, sd); + __FUNCTION__, sk, assoc, newsock->sk, retval); /* Return the fd mapped to the new socket. */ - peeloff.sd = sd; + peeloff.sd = retval; if (copy_to_user(optval, &peeloff, len)) - return -EFAULT; + retval = -EFAULT; - return 0; +out_unlock: + sctp_release_sock(sk); + return retval; } SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname, -- cgit v1.2.3 From 0ab86310a8caafd178c8722357680e9db647e579 Mon Sep 17 00:00:00 2001 From: Sridhar Samudrala Date: Tue, 15 Oct 2002 02:33:01 -0700 Subject: sctp: VTAG checks for ABORT & SHUTDOWN_COMPLETE chunks (ardelle.fan) --- include/net/sctp/sm.h | 32 ++++++++++++++++++++++++++++++++ net/sctp/sm_statefuns.c | 36 ++++++++++++++++++++---------------- 2 files changed, 52 insertions(+), 16 deletions(-) (limited to 'include/net') diff --git a/include/net/sctp/sm.h b/include/net/sctp/sm.h index fc9940ff2651..dcbe05457478 100644 --- a/include/net/sctp/sm.h +++ b/include/net/sctp/sm.h @@ -432,4 +432,36 @@ static inline void sctp_add_cmd_sf(sctp_cmd_seq_t *seq, sctp_verb_t verb, sctp_a BUG(); } +/* Check VTAG of the packet matches the sender's own tag OR its peer's + * tag and the T bit is set in the Chunk Flags. + */ +static inline int +sctp_vtag_verify_either(const sctp_chunk_t *chunk, + const sctp_association_t *asoc) +{ + /* RFC 2960 Section 8.5.1, sctpimpguide-06 Section 2.13.2 + * + * B) The receiver of a ABORT shall accept the packet if the + * Verification Tag field of the packet matches its own tag OR it + * is set to its peer's tag and the T bit is set in the Chunk + * Flags. Otherwise, the receiver MUST silently discard the packet + * and take no further action. + * + * (C) The receiver of a SHUTDOWN COMPLETE shall accept the + * packet if the Verification Tag field of the packet + * matches its own tag OR it is set to its peer's tag and + * the T bit is set in the Chunk Flags. Otherwise, the + * receiver MUST silently discard the packet and take no + * further action.... + * + */ + if ((ntohl(chunk->sctp_hdr->vtag) == asoc->c.my_vtag) || + (sctp_test_T_bit(chunk) && (ntohl(chunk->sctp_hdr->vtag) + == asoc->c.peer_vtag))) { + return 1; + } + + return 0; +} + #endif /* __sctp_sm_h__ */ diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index a002b82b1cec..1e606a2243f2 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -110,18 +110,7 @@ sctp_disposition_t sctp_sf_do_4_C(const sctp_endpoint_t *ep, if (!chunk->singleton) return SCTP_DISPOSITION_VIOLATION; - /* RFC 2960 8.5.1 Exceptions in Verification Tag Rules - * - * (C) The receiver of a SHUTDOWN COMPLETE shall accept the - * packet if the Verification Tag field of the packet - * matches its own tag OR it is set to its peer's tag and - * the T bit is set in the Chunk Flags. Otherwise, the - * receiver MUST silently discard the packet and take no - * further action.... - */ - if ((ntohl(chunk->sctp_hdr->vtag) != asoc->c.my_vtag) && - !(sctp_test_T_bit(chunk) || - (ntohl(chunk->sctp_hdr->vtag) != asoc->peer.i.init_tag))) + if (!sctp_vtag_verify_either(chunk, asoc)) return sctp_sf_pdiscard(ep, asoc, type, arg, commands); /* RFC 2960 10.2 SCTP-to-ULP @@ -1636,6 +1625,10 @@ sctp_disposition_t sctp_sf_shutdown_pending_abort(const sctp_endpoint_t *ep, void *arg, sctp_cmd_seq_t *commands) { + sctp_chunk_t *chunk = arg; + + if (!sctp_vtag_verify_either(chunk, asoc)) + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); /* Stop the T5-shutdown guard timer. */ sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, @@ -1655,6 +1648,10 @@ sctp_disposition_t sctp_sf_shutdown_sent_abort(const sctp_endpoint_t *ep, void *arg, sctp_cmd_seq_t *commands) { + sctp_chunk_t *chunk = arg; + + if (!sctp_vtag_verify_either(chunk, asoc)) + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); /* Stop the T2-shutdown timer. */ sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, @@ -1826,7 +1823,12 @@ sctp_disposition_t sctp_sf_do_9_1_abort(const sctp_endpoint_t *ep, void *arg, sctp_cmd_seq_t *commands) { - /* ASSOC_FAILED will DELETE_TCB. */ + sctp_chunk_t *chunk = arg; + + if (!sctp_vtag_verify_either(chunk, asoc)) + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + + /* ASSOC_FAILED will DELETE_TCB. */ sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_NULL()); /* BUG? This does not look complete... */ @@ -1844,8 +1846,10 @@ sctp_disposition_t sctp_sf_cookie_wait_abort(const sctp_endpoint_t *ep, void *arg, sctp_cmd_seq_t *commands) { - /* Check the verification tag. */ - /* BUG: WRITE ME. */ + sctp_chunk_t *chunk = arg; + + if (!sctp_vtag_verify_either(chunk, asoc)) + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, SCTP_STATE(SCTP_STATE_CLOSED)); @@ -4218,7 +4222,7 @@ sctp_packet_t *sctp_ootb_pkt_new(const sctp_association_t *asoc, /* Special case the INIT as there is no vtag yet. */ if (SCTP_CID_INIT == chunk->chunk_hdr->type) { sctp_init_chunk_t *init; - init = (sctp_init_chunk_t *)&chunk->chunk_hdr; + init = (sctp_init_chunk_t *)chunk->chunk_hdr; vtag = ntohl(init->init_hdr.init_tag); } else { vtag = ntohl(chunk->sctp_hdr->vtag); -- cgit v1.2.3 From 33db3af1fffcc49045732d4040942790d6975f32 Mon Sep 17 00:00:00 2001 From: Sridhar Samudrala Date: Tue, 15 Oct 2002 03:46:03 -0700 Subject: sctp: Fixes Bug#623286 - zero vtag in SHUTDOWN_COMPLETE chunk (samudrala) --- include/net/sctp/sm.h | 1 + net/sctp/input.c | 2 +- net/sctp/sm_statefuns.c | 32 +++++++++++++++++++++++++------- net/sctp/sm_statetable.c | 4 ++-- 4 files changed, 29 insertions(+), 10 deletions(-) (limited to 'include/net') diff --git a/include/net/sctp/sm.h b/include/net/sctp/sm.h index dcbe05457478..36ed5af39824 100644 --- a/include/net/sctp/sm.h +++ b/include/net/sctp/sm.h @@ -139,6 +139,7 @@ sctp_state_fn_t sctp_sf_do_5_2_1_siminit; sctp_state_fn_t sctp_sf_do_5_2_2_dupinit; sctp_state_fn_t sctp_sf_do_5_2_4_dupcook; sctp_state_fn_t sctp_sf_unk_chunk; +sctp_state_fn_t sctp_sf_do_8_5_1_E_sa; /* Prototypes for primitive event state functions. */ sctp_state_fn_t sctp_sf_do_prm_asoc; diff --git a/net/sctp/input.c b/net/sctp/input.c index 7a83c77dbdf9..641a8cdcc115 100644 --- a/net/sctp/input.c +++ b/net/sctp/input.c @@ -578,7 +578,7 @@ int sctp_has_association(const sockaddr_storage_t *laddr, sctp_association_t *asoc; sctp_transport_t *transport; - if (asoc = sctp_lookup_association(laddr, paddr, &transport)) { + if ((asoc = sctp_lookup_association(laddr, paddr, &transport))) { sock_put(asoc->base.sk); sctp_association_put(asoc); return 1; diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 1e606a2243f2..436638e2fccc 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -2775,13 +2775,6 @@ sctp_disposition_t sctp_sf_ootb(const sctp_endpoint_t *ep, * the Verification Tag received in the SHUTDOWN ACK and set the * T-bit in the Chunk Flags to indicate that no TCB was found. * - * Verification Tag: 8.5.1 E) Rules for packet carrying a SHUTDOWN ACK - * If the receiver is in COOKIE-ECHOED or COOKIE-WAIT state the - * procedures in section 8.4 SHOULD be followed, in other words it - * should be treated as an Out Of The Blue packet. - * [This means that we do NOT check the Verification Tag on these - * chunks. --piggy ] - * * Inputs * (endpoint, asoc, type, arg, commands) * @@ -2826,6 +2819,31 @@ sctp_disposition_t sctp_sf_shut_8_4_5(const sctp_endpoint_t *ep, return SCTP_DISPOSITION_NOMEM; } +/* + * Handle SHUTDOWN ACK in COOKIE_ECHOED or COOKIE_WAIT state. + * + * Verification Tag: 8.5.1 E) Rules for packet carrying a SHUTDOWN ACK + * If the receiver is in COOKIE-ECHOED or COOKIE-WAIT state the + * procedures in section 8.4 SHOULD be followed, in other words it + * should be treated as an Out Of The Blue packet. + * [This means that we do NOT check the Verification Tag on these + * chunks. --piggy ] + * + */ +sctp_disposition_t sctp_sf_do_8_5_1_E_sa(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + /* Although we do have an association in this case, it corresponds + * to a restarted association. So the packet is treated as an OOTB + * packet and the state function that handles OOTB SHUTDOWN_ACK is + * called with a NULL association. + */ + return sctp_sf_shut_8_4_5(ep, NULL, type, arg, commands); +} + /* * Process an unknown chunk. * diff --git a/net/sctp/sm_statetable.c b/net/sctp/sm_statetable.c index 9f1acdb839ad..9b3d161100d6 100644 --- a/net/sctp/sm_statetable.c +++ b/net/sctp/sm_statetable.c @@ -271,9 +271,9 @@ sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type, /* SCTP_STATE_CLOSED */ \ {.fn = sctp_sf_ootb, .name = "sctp_sf_ootb"}, \ /* SCTP_STATE_COOKIE_WAIT */ \ - {.fn = sctp_sf_ootb, .name = "sctp_sf_ootb"}, \ + {.fn = sctp_sf_do_8_5_1_E_sa, .name = "sctp_sf_do_8_5_1_E_sa"}, \ /* SCTP_STATE_COOKIE_ECHOED */ \ - {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \ + {.fn = sctp_sf_do_8_5_1_E_sa, .name = "sctp_sf_do_8_5_1_E_sa"}, \ /* SCTP_STATE_ESTABLISHED */ \ {.fn = sctp_sf_violation, .name = "sctp_sf_violation"}, \ /* SCTP_STATE_SHUTDOWN_PENDING */ \ -- cgit v1.2.3 From ad0631ac2702d2c48636c1b457f3b32dbeddda9c Mon Sep 17 00:00:00 2001 From: Sridhar Samudrala Date: Wed, 16 Oct 2002 21:25:24 -0700 Subject: sctp: Fixes a bug in the calculation of highest new tsn in sack. --- include/net/sctp/structs.h | 3 ++ net/sctp/associola.c | 1 + net/sctp/outqueue.c | 85 +++++++++++++++++++++++++++++++++------------- 3 files changed, 66 insertions(+), 23 deletions(-) (limited to 'include/net') diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 04288b3a7814..5a8b75d2da26 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -1315,6 +1315,9 @@ struct SCTP_association { __u32 ctsn_ack_point; + /* Highest TSN that is acknowledged by incoming SACKs. */ + __u32 highest_sacked; + /* The number of unacknowledged data chunks. Reported through * the SCTP_STATUS sockopt. */ diff --git a/net/sctp/associola.c b/net/sctp/associola.c index 40e1f7c685e5..9d68ddf0fa56 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -207,6 +207,7 @@ sctp_association_t *sctp_association_init(sctp_association_t *asoc, asoc->next_tsn = asoc->c.initial_tsn; asoc->ctsn_ack_point = asoc->next_tsn - 1; + asoc->highest_sacked = asoc->ctsn_ack_point; asoc->unack_data = 0; diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c index 94ebe7f08b53..6cec3d5bc045 100644 --- a/net/sctp/outqueue.c +++ b/net/sctp/outqueue.c @@ -59,7 +59,8 @@ static int sctp_acked(sctp_sackhdr_t *sack, __u32 tsn); static void sctp_check_transmitted(sctp_outqueue_t *q, struct list_head *transmitted_queue, sctp_transport_t *transport, - sctp_sackhdr_t *sack); + sctp_sackhdr_t *sack, + __u32 highest_new_tsn); /* Generate a new outqueue. */ sctp_outqueue_t *sctp_outqueue_new(sctp_association_t *asoc) @@ -971,6 +972,36 @@ static void sctp_sack_update_unack_data(sctp_association_t *assoc, assoc->unack_data = unack_data; } +/* Return the highest new tsn that is acknowledged by the given SACK chunk. */ +static __u32 sctp_highest_new_tsn(sctp_sackhdr_t *sack, + sctp_association_t *asoc) +{ + struct list_head *ltransport, *lchunk; + sctp_transport_t *transport; + sctp_chunk_t *chunk; + __u32 highest_new_tsn, tsn; + struct list_head *transport_list = &asoc->peer.transport_addr_list; + + highest_new_tsn = ntohl(sack->cum_tsn_ack); + + list_for_each(ltransport, transport_list) { + transport = list_entry(ltransport, sctp_transport_t, + transports); + list_for_each(lchunk, &transport->transmitted) { + chunk = list_entry(lchunk, sctp_chunk_t, + transmitted_list); + tsn = ntohl(chunk->subh.data_hdr->tsn); + + if (!chunk->tsn_gap_acked && + TSN_lt(highest_new_tsn, tsn) && + sctp_acked(sack, tsn)) + highest_new_tsn = tsn; + } + } + + return highest_new_tsn; +} + /* This is where we REALLY process a SACK. * * Process the sack against the outqueue. Mostly, this just frees @@ -978,22 +1009,36 @@ static void sctp_sack_update_unack_data(sctp_association_t *assoc, */ int sctp_sack_outqueue(sctp_outqueue_t *q, sctp_sackhdr_t *sack) { + sctp_association_t *asoc = q->asoc; + sctp_transport_t *transport; sctp_chunk_t *tchunk; struct list_head *lchunk, *transport_list, *pos; - __u32 tsn; - __u32 sack_ctsn; - __u32 ctsn; - sctp_transport_t *transport; - int outstanding; + sctp_sack_variable_t *frags = sack->variable; + __u32 sack_ctsn, ctsn, tsn; + __u32 highest_tsn, highest_new_tsn; __u32 sack_a_rwnd; + int outstanding; /* Grab the association's destination address list. */ - transport_list = &q->asoc->peer.transport_addr_list; + transport_list = &asoc->peer.transport_addr_list; + + sack_ctsn = ntohl(sack->cum_tsn_ack); + + /* Get the highest TSN in the sack. */ + highest_tsn = sack_ctsn + + ntohs(frags[ntohs(sack->num_gap_ack_blocks) - 1].gab.end); + + if (TSN_lt(asoc->highest_sacked, highest_tsn)) { + highest_new_tsn = highest_tsn; + asoc->highest_sacked = highest_tsn; + } else { + highest_new_tsn = sctp_highest_new_tsn(sack, asoc); + } /* Run through the retransmit queue. Credit bytes received * and free those chunks that we can. */ - sctp_check_transmitted(q, &q->retransmit, NULL, sack); + sctp_check_transmitted(q, &q->retransmit, NULL, sack, highest_new_tsn); /* Run through the transmitted queue. * Credit bytes received and free those chunks which we can. @@ -1003,23 +1048,22 @@ int sctp_sack_outqueue(sctp_outqueue_t *q, sctp_sackhdr_t *sack) list_for_each(pos, transport_list) { transport = list_entry(pos, sctp_transport_t, transports); sctp_check_transmitted(q, &transport->transmitted, - transport, sack); + transport, sack, highest_new_tsn); } /* Move the Cumulative TSN Ack Point if appropriate. */ - sack_ctsn = ntohl(sack->cum_tsn_ack); - if (TSN_lt(q->asoc->ctsn_ack_point, sack_ctsn)) - q->asoc->ctsn_ack_point = sack_ctsn; + if (TSN_lt(asoc->ctsn_ack_point, sack_ctsn)) + asoc->ctsn_ack_point = sack_ctsn; /* Update unack_data field in the assoc. */ - sctp_sack_update_unack_data(q->asoc, sack); + sctp_sack_update_unack_data(asoc, sack); - ctsn = q->asoc->ctsn_ack_point; + ctsn = asoc->ctsn_ack_point; SCTP_DEBUG_PRINTK("%s: sack Cumulative TSN Ack is 0x%x.\n", __FUNCTION__, sack_ctsn); SCTP_DEBUG_PRINTK("%s: Cumulative TSN Ack of association " - "%p is 0x%x.\n", __FUNCTION__, q->asoc, ctsn); + "%p is 0x%x.\n", __FUNCTION__, asoc, ctsn); /* Throw away stuff rotting on the sack queue. */ list_for_each(lchunk, &q->sacked) { @@ -1045,7 +1089,7 @@ int sctp_sack_outqueue(sctp_outqueue_t *q, sctp_sackhdr_t *sack) sack_a_rwnd = 0; } - q->asoc->peer.rwnd = sack_a_rwnd; + asoc->peer.rwnd = sack_a_rwnd; /* See if all chunks are acked. * Make sure the empty queue handler will get run later. @@ -1092,7 +1136,8 @@ int sctp_outqueue_is_empty(const sctp_outqueue_t *q) static void sctp_check_transmitted(sctp_outqueue_t *q, struct list_head *transmitted_queue, sctp_transport_t *transport, - sctp_sackhdr_t *sack) + sctp_sackhdr_t *sack, + __u32 highest_new_tsn_in_sack) { struct list_head *lchunk; sctp_chunk_t *tchunk; @@ -1100,7 +1145,6 @@ static void sctp_check_transmitted(sctp_outqueue_t *q, __u32 tsn; __u32 sack_ctsn; __u32 rtt; - __u32 highest_new_tsn_in_sack; __u8 restart_timer = 0; __u8 do_fast_retransmit = 0; int bytes_acked = 0; @@ -1121,7 +1165,6 @@ static void sctp_check_transmitted(sctp_outqueue_t *q, #endif /* SCTP_DEBUG */ sack_ctsn = ntohl(sack->cum_tsn_ack); - highest_new_tsn_in_sack = sack_ctsn; INIT_LIST_HEAD(&tlist); @@ -1194,10 +1237,6 @@ static void sctp_check_transmitted(sctp_outqueue_t *q, if (!tchunk->tsn_gap_acked) { tchunk->tsn_gap_acked = 1; bytes_acked += sctp_data_size(tchunk); - if (TSN_lt(highest_new_tsn_in_sack, - tsn)) { - highest_new_tsn_in_sack = tsn; - } } list_add_tail(lchunk, &tlist); } -- cgit v1.2.3