summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/net/sctp/sm.h37
-rw-r--r--include/net/sctp/structs.h8
-rw-r--r--net/sctp/associola.c8
-rw-r--r--net/sctp/bind_addr.c32
-rw-r--r--net/sctp/input.c65
-rw-r--r--net/sctp/output.c14
-rw-r--r--net/sctp/outqueue.c163
-rw-r--r--net/sctp/sm_make_chunk.c67
-rw-r--r--net/sctp/sm_sideeffect.c6
-rw-r--r--net/sctp/sm_statefuns.c294
-rw-r--r--net/sctp/sm_statetable.c4
-rw-r--r--net/sctp/socket.c92
12 files changed, 534 insertions, 256 deletions
diff --git a/include/net/sctp/sm.h b/include/net/sctp/sm.h
index 9459322a411b..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;
@@ -329,10 +330,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);
@@ -432,4 +433,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/include/net/sctp/structs.h b/include/net/sctp/structs.h
index d1748a73e6b0..5a8b75d2da26 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
@@ -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,
@@ -1312,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 5c8f52eb27a0..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;
@@ -476,13 +477,6 @@ sctp_transport_t *sctp_assoc_add_peer(sctp_association_t *asoc,
asoc->peer.retran_path = peer;
}
- /* If we do not yet have a primary path, set one. */
- if (NULL == asoc->peer.primary_path) {
- asoc->peer.primary_path = peer;
- asoc->peer.active_path = peer;
- asoc->peer.retran_path = peer;
- }
-
if (asoc->peer.active_path == asoc->peer.retran_path)
asoc->peer.retran_path = peer;
diff --git a/net/sctp/bind_addr.c b/net/sctp/bind_addr.c
index 7a1e4f2693ce..1c70d05e2c33 100644
--- a/net/sctp/bind_addr.c
+++ b/net/sctp/bind_addr.c
@@ -199,11 +199,10 @@ int sctp_del_bind_addr(sctp_bind_addr_t *bp, sockaddr_storage_t *del_addr)
sctpParam_t sctp_bind_addrs_to_raw(const sctp_bind_addr_t *bp, int *addrs_len,
int priority)
{
- sctpParam_t rawaddr;
sctpParam_t addrparms;
sctpParam_t retval;
int addrparms_len;
- sctpIpAddress_t rawaddr_space;
+ sctp_addr_param_t rawaddr;
int len;
struct sockaddr_storage_list *addr;
struct list_head *pos;
@@ -214,7 +213,7 @@ sctpParam_t sctp_bind_addrs_to_raw(const sctp_bind_addr_t *bp, int *addrs_len,
/* Allocate enough memory at once. */
list_for_each(pos, &bp->address_list) {
- len += sizeof(sctp_ipv6addr_param_t);
+ len += sizeof(sctp_addr_param_t);
}
addrparms.v = kmalloc(len, priority);
@@ -222,12 +221,11 @@ sctpParam_t sctp_bind_addrs_to_raw(const sctp_bind_addr_t *bp, int *addrs_len,
goto end_raw;
retval = addrparms;
- rawaddr.v4 = &rawaddr_space.v4;
list_for_each(pos, &bp->address_list) {
addr = list_entry(pos, struct sockaddr_storage_list, list);
- len = sockaddr2sctp_addr(&addr->a, rawaddr);
- memcpy(addrparms.v, rawaddr.v, len);
+ len = sockaddr2sctp_addr(&addr->a, &rawaddr);
+ memcpy(addrparms.v, &rawaddr, len);
addrparms.v += len;
addrparms_len += len;
}
@@ -244,33 +242,39 @@ end_raw:
int sctp_raw_to_bind_addrs(sctp_bind_addr_t *bp, __u8 *raw_addr_list,
int addrs_len, __u16 port, int priority)
{
- sctpParam_t rawaddr;
+ sctp_addr_param_t *rawaddr;
+ sctp_paramhdr_t *param;
sockaddr_storage_t addr;
int retval = 0;
int len;
/* Convert the raw address to standard address format */
while (addrs_len) {
- rawaddr.v = raw_addr_list;
- if (SCTP_PARAM_IPV4_ADDRESS==rawaddr.p->type
- || SCTP_PARAM_IPV6_ADDRESS==rawaddr.p->type) {
+ param = (sctp_paramhdr_t *)raw_addr_list;
+ rawaddr = (sctp_addr_param_t *)raw_addr_list;
+
+ switch (param->type) {
+ case SCTP_PARAM_IPV4_ADDRESS:
+ case SCTP_PARAM_IPV6_ADDRESS:
sctp_param2sockaddr(&addr, rawaddr, port);
retval = sctp_add_bind_addr(bp, &addr, priority);
if (retval) {
/* Can't finish building the list, clean up. */
sctp_bind_addr_clean(bp);
- break;
+ break;;
}
-
- len = ntohs(rawaddr.p->length);
+ len = ntohs(param->length);
addrs_len -= len;
raw_addr_list += len;
- } else {
+ break;
+ default:
/* Corrupted raw addr list! */
retval = -EINVAL;
sctp_bind_addr_clean(bp);
break;
}
+ if (retval)
+ break;
}
return retval;
diff --git a/net/sctp/input.c b/net/sctp/input.c
index 8d1ba1b65cc1..8ed4aaaf085e 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.
@@ -584,14 +614,20 @@ static sctp_association_t *__sctp_rcv_initack_lookup(struct sk_buff *skb,
struct sctphdr *sh = (struct sctphdr *) skb->h.raw;
sctp_chunkhdr_t *ch;
__u8 *ch_end, *data;
- sctpParam_t parm;
+ sctp_paramhdr_t *parm;
ch = (sctp_chunkhdr_t *) skb->data;
ch_end = ((__u8 *) ch) + WORD_ROUND(ntohs(ch->length));
- if (SCTP_CID_INIT_ACK != ch->type)
+ /* If this is INIT/INIT-ACK look inside the chunk too. */
+ switch (ch->type) {
+ case SCTP_CID_INIT:
+ case SCTP_CID_INIT_ACK:
+ break;
+ default:
return NULL;
+ }
/*
* This code will NOT touch anything inside the chunk--it is
@@ -609,26 +645,25 @@ static sctp_association_t *__sctp_rcv_initack_lookup(struct sk_buff *skb,
/* Find the start of the TLVs and the end of the chunk. This is
* the region we search for address parameters.
*/
-
data = skb->data + sizeof(sctp_init_chunk_t);
/* See sctp_process_init() for how to go thru TLVs. */
while (data < ch_end) {
- parm.v = data;
+ parm = (sctp_paramhdr_t *)data;
- if (!parm.p->length)
+ if (!parm->length)
break;
- data += WORD_ROUND(ntohs(parm.p->length));
+ data += WORD_ROUND(ntohs(parm->length));
/* Note: Ignoring hostname addresses. */
- if ((SCTP_PARAM_IPV4_ADDRESS != parm.p->type) &&
- (SCTP_PARAM_IPV6_ADDRESS != parm.p->type))
+ if ((SCTP_PARAM_IPV4_ADDRESS != parm->type) &&
+ (SCTP_PARAM_IPV6_ADDRESS != parm->type))
continue;
- sctp_param2sockaddr(paddr, parm, ntohs(sh->source));
-
- asoc = __sctp_rcv_lookup_association(laddr, paddr, transportp);
+ sctp_param2sockaddr(paddr, (sctp_addr_param_t *)parm,
+ ntohs(sh->source));
+ asoc = __sctp_lookup_association(laddr, paddr, transportp);
if (asoc)
return asoc;
}
@@ -644,7 +679,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/output.c b/net/sctp/output.c
index c0f682004baf..966fcddfb974 100644
--- a/net/sctp/output.c
+++ b/net/sctp/output.c
@@ -530,9 +530,17 @@ static sctp_xmit_t sctp_packet_append_data(sctp_packet_t *packet,
* to a given transport address if it has cwnd or more bytes
* of data outstanding to that transport address.
*/
- if (transport->flight_size >= transport->cwnd) {
- retval = SCTP_XMIT_RWND_FULL;
- goto finish;
+ /* RFC 7.2.4 & the Implementers Guide 2.8.
+ *
+ * 3) ...
+ * When a Fast Retransmit is being performed the sender SHOULD
+ * ignore the value of cwnd and SHOULD NOT delay retransmission.
+ */
+ if (!chunk->fast_retransmit) {
+ if (transport->flight_size >= transport->cwnd) {
+ retval = SCTP_XMIT_RWND_FULL;
+ goto finish;
+ }
}
/* Keep track of how many bytes are in flight over this transport. */
diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c
index 94ebe7f08b53..1a7b84311f0a 100644
--- a/net/sctp/outqueue.c
+++ b/net/sctp/outqueue.c
@@ -3,45 +3,45 @@
* Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001 Intel Corp.
* Copyright (c) 2001-2002 International Business Machines Corp.
- *
+ *
* This file is part of the SCTP kernel reference Implementation
- *
+ *
* These functions implement the outqueue class. The outqueue handles
- * bundling and queueing of outgoing SCTP chunks.
- *
- * The SCTP reference implementation is free software;
- * you can redistribute it and/or modify it under the terms of
+ * bundling and queueing of outgoing SCTP chunks.
+ *
+ * The SCTP reference 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
+ *
+ * The SCTP reference 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.
* See the GNU General Public License for more details.
- *
+ *
* You should have received a copy of the GNU General Public License
* along with GNU CC; see the file COPYING. If not, write to
* the Free Software Foundation, 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
+ * Boston, MA 02111-1307, USA.
+ *
* Please send any bug reports or fixes you make to the
* email address(es):
* lksctp developers <lksctp-developers@lists.sourceforge.net>
- *
+ *
* Or submit a bug report through the following website:
* http://www.sf.net/projects/lksctp
*
- * Written or modified by:
+ * Written or modified by:
* La Monte H.P. Yarroll <piggy@acm.org>
- * Karl Knutson <karl@athena.chicago.il.us>
+ * Karl Knutson <karl@athena.chicago.il.us>
* Perry Melange <pmelange@null.cc.uic.edu>
* Xingang Guo <xingang.guo@intel.com>
* Hui Huang <hui.huang@nokia.com>
* Sridhar Samudrala <sri@us.ibm.com>
* Jon Grimm <jgrimm@us.ibm.com>
- *
+ *
* Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release.
*/
@@ -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)
@@ -163,7 +164,7 @@ int sctp_push_outqueue(sctp_outqueue_t *q, sctp_chunk_t *chunk)
if (SCTP_CID_DATA == chunk->chunk_hdr->type) {
/* Is it OK to queue data chunks? */
/* From 9. Termination of Association
- *
+ *
* When either endpoint performs a shutdown, the
* association on each peer will stop accepting new
* data from its user and only deliver data in queue
@@ -276,10 +277,10 @@ void sctp_retransmit_mark(sctp_outqueue_t *q, sctp_transport_t *transport,
}
-/* Mark all the eligible packets on a transport for retransmission and force
+/* Mark all the eligible packets on a transport for retransmission and force
* one packet out.
*/
-void sctp_retransmit(sctp_outqueue_t *q, sctp_transport_t *transport,
+void sctp_retransmit(sctp_outqueue_t *q, sctp_transport_t *transport,
__u8 fast_retransmit)
{
int error = 0;
@@ -514,7 +515,7 @@ void sctp_xmit_fragmented_chunks(sctp_outqueue_t *q, sctp_packet_t *packet,
* fragments. It returns the first fragment with the frag_list field holding
* the remaining fragments.
*/
-sctp_chunk_t *sctp_fragment_chunk(sctp_chunk_t *chunk,
+sctp_chunk_t *sctp_fragment_chunk(sctp_chunk_t *chunk,
size_t max_frag_data_len)
{
sctp_association_t *asoc = chunk->asoc;
@@ -549,12 +550,12 @@ sctp_chunk_t *sctp_fragment_chunk(sctp_chunk_t *chunk,
/* Make the middle fragments. */
while (chunk_data_len > max_frag_data_len) {
frag = sctp_make_datafrag(asoc, sinfo, max_frag_data_len,
- data_ptr, SCTP_DATA_MIDDLE_FRAG,
+ data_ptr, SCTP_DATA_MIDDLE_FRAG,
ssn);
if (!frag)
goto err;
- /* Add the middle fragment to the first fragment's
+ /* Add the middle fragment to the first fragment's
* frag_list.
*/
list_add_tail(&frag->frag_list, frag_list);
@@ -773,6 +774,12 @@ int sctp_flush_outqueue(sctp_outqueue_t *q, int rtx_timeout)
*/
if (packet->has_cookie_echo)
goto sctp_flush_out;
+
+ /* Don't send new data if there is still data
+ * waiting to retransmit.
+ */
+ if (!list_empty(&q->retransmit))
+ goto sctp_flush_out;
}
/* Finally, transmit new packets. */
@@ -849,9 +856,9 @@ int sctp_flush_outqueue(sctp_outqueue_t *q, int rtx_timeout)
/* We could not append this chunk, so put
* the chunk back on the output queue.
*/
- SCTP_DEBUG_PRINTK("sctp_flush_outqueue: could"
+ SCTP_DEBUG_PRINTK("sctp_flush_outqueue: could "
"not transmit TSN: 0x%x, status: %d\n",
- ntohl(chunk->subh.data_hdr->tsn),
+ ntohl(chunk->subh.data_hdr->tsn),
status);
skb_queue_head(queue, (struct sk_buff *)chunk);
goto sctp_flush_out;
@@ -911,7 +918,7 @@ int sctp_flush_outqueue(sctp_outqueue_t *q, int rtx_timeout)
default:
/* Do nothing. */
- break;
+ break;
}
sctp_flush_out:
@@ -932,7 +939,7 @@ sctp_flush_out:
if (packet->size != SCTP_IP_OVERHEAD)
error = (*q->force_output)(packet);
}
-
+
return error;
}
@@ -971,6 +978,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 +1015,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 +1054,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 +1095,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.
@@ -1054,14 +1104,14 @@ int sctp_sack_outqueue(sctp_outqueue_t *q, sctp_sackhdr_t *sack)
if (!q->empty)
goto finish;
- list_for_each(pos, transport_list) {
+ list_for_each(pos, transport_list) {
transport = list_entry(pos, sctp_transport_t, transports);
q->empty = q->empty && list_empty(&transport->transmitted);
if (!q->empty)
goto finish;
}
- SCTP_DEBUG_PRINTK("sack queue is empty.\n");
+ SCTP_DEBUG_PRINTK("sack queue is empty.\n");
finish:
return q->empty;
}
@@ -1092,7 +1142,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 +1151,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 +1171,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);
@@ -1144,8 +1193,8 @@ static void sctp_check_transmitted(sctp_outqueue_t *q,
* 6.3.1 C5) Karn's algorithm: RTT measurements
* MUST NOT be made using packets that were
* retransmitted (and thus for which it is
- * ambiguous whether the reply was for the
- * first instance of the packet or a later
+ * ambiguous whether the reply was for the
+ * first instance of the packet or a later
* instance).
*/
if ((!tchunk->tsn_gap_acked) &&
@@ -1181,23 +1230,19 @@ static void sctp_check_transmitted(sctp_outqueue_t *q,
* 'Stray DATA chunk(s)' record the highest TSN
* reported as newly acknowledged, call this
* value 'HighestTSNinSack'. A newly
- * acknowledged DATA chunk is one not
+ * acknowledged DATA chunk is one not
* previously acknowledged in a SACK.
*
* When the SCTP sender of data receives a SACK
* chunk that acknowledges, for the first time,
* the receipt of a DATA chunk, all the still
- * unacknowledged DATA chunks whose TSN is
- * older than that newly acknowledged DATA
+ * unacknowledged DATA chunks whose TSN is
+ * 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);
- if (TSN_lt(highest_new_tsn_in_sack,
- tsn)) {
- highest_new_tsn_in_sack = tsn;
- }
}
list_add_tail(lchunk, &tlist);
}
@@ -1258,10 +1303,10 @@ static void sctp_check_transmitted(sctp_outqueue_t *q,
/* RFC 2960 6.3.2 Retransmission Timer Rules
*
- * R4) Whenever a SACK is received missing a
- * TSN that was previously acknowledged via a
- * Gap Ack Block, start T3-rtx for the
- * destination address to which the DATA
+ * R4) Whenever a SACK is received missing a
+ * TSN that was previously acknowledged via a
+ * Gap Ack Block, start T3-rtx for the
+ * destination address to which the DATA
* chunk was originally
* transmitted if it is not already running.
*/
diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c
index 74c795b964f5..58c2a7de57f4 100644
--- a/net/sctp/sm_make_chunk.c
+++ b/net/sctp/sm_make_chunk.c
@@ -1454,7 +1454,7 @@ int sctp_verify_init(const sctp_association_t *asoc,
return 0;
} /* for (loop through all parameters) */
-
+
return 1;
}
@@ -1710,6 +1710,7 @@ int sctp_process_param(sctp_association_t *asoc, sctpParam_t param,
sctp_cid_t cid, int priority)
{
sockaddr_storage_t addr;
+ sctp_addr_param_t *addrparm;
int j;
int i;
int retval = 1;
@@ -1721,24 +1722,23 @@ int sctp_process_param(sctp_association_t *asoc, sctpParam_t param,
*/
switch (param.p->type) {
case SCTP_PARAM_IPV4_ADDRESS:
- if (SCTP_CID_INIT != cid) {
- sctp_param2sockaddr(&addr, param, asoc->peer.port);
- scope = sctp_scope(peer_addr);
- if (sctp_in_scope(&addr, scope))
- sctp_assoc_add_peer(asoc, &addr, priority);
- }
+ addrparm = (sctp_addr_param_t *)param.v;
+ sctp_param2sockaddr(&addr, addrparm, asoc->peer.port);
+ scope = sctp_scope(peer_addr);
+ if (sctp_in_scope(&addr, scope))
+ sctp_assoc_add_peer(asoc, &addr, priority);
break;
case SCTP_PARAM_IPV6_ADDRESS:
- if (SCTP_CID_INIT != cid) {
- if (PF_INET6 == asoc->base.sk->family) {
- sctp_param2sockaddr(&addr, param,
- asoc->peer.port);
- scope = sctp_scope(peer_addr);
- if (sctp_in_scope(&addr, scope))
- sctp_assoc_add_peer(asoc, &addr,
- priority);
- }
+ /* Rethink this as we may need to keep for
+ * restart considerations.
+ */
+ if (PF_INET6 == asoc->base.sk->family) {
+ addrparm = (sctp_addr_param_t *)param.v;
+ sctp_param2sockaddr(&addr, addrparm, asoc->peer.port);
+ scope = sctp_scope(peer_addr);
+ if (sctp_in_scope(&addr, scope))
+ sctp_assoc_add_peer(asoc, &addr, priority);
}
break;
@@ -1833,8 +1833,7 @@ __u32 sctp_generate_tag(const sctp_endpoint_t *ep)
/* Select an initial TSN to send during startup. */
__u32 sctp_generate_tsn(const sctp_endpoint_t *ep)
{
- /* I believe that this random number generator complies with RFC1750. */
- __u32 retval;
+ __u32 retval;
get_random_bytes(&retval, sizeof(__u32));
return retval;
@@ -1845,26 +1844,27 @@ __u32 sctp_generate_tsn(const sctp_endpoint_t *ep)
********************************************************************/
/* Convert from an SCTP IP parameter to a sockaddr_storage_t. */
-void sctp_param2sockaddr(sockaddr_storage_t *addr, sctpParam_t param, __u16 port)
+void sctp_param2sockaddr(sockaddr_storage_t *addr, sctp_addr_param_t *param,
+ __u16 port)
{
- switch(param.p->type) {
+ switch(param->v4.param_hdr.type) {
case SCTP_PARAM_IPV4_ADDRESS:
addr->v4.sin_family = AF_INET;
addr->v4.sin_port = port;
- addr->v4.sin_addr.s_addr = param.v4->addr.s_addr;
+ addr->v4.sin_addr.s_addr = param->v4.addr.s_addr;
break;
case SCTP_PARAM_IPV6_ADDRESS:
addr->v6.sin6_family = AF_INET6;
addr->v6.sin6_port = port;
addr->v6.sin6_flowinfo = 0; /* BUG */
- addr->v6.sin6_addr = param.v6->addr;
+ addr->v6.sin6_addr = param->v6.addr;
addr->v6.sin6_scope_id = 0; /* BUG */
break;
default:
SCTP_DEBUG_PRINTK("Illegal address type %d\n",
- ntohs(param.p->type));
+ ntohs(param->v4.param_hdr.type));
break;
};
}
@@ -1904,11 +1904,9 @@ int ipver2af(__u8 ipver)
case 4:
family = AF_INET;
break;
-
case 6:
family = AF_INET6;
break;
-
default:
family = 0;
break;
@@ -1917,25 +1915,26 @@ int ipver2af(__u8 ipver)
return family;
}
-/* Convert a sockaddr_in to IP address in an SCTP para. */
-/* Returns true if a valid conversion was possible. */
-int sockaddr2sctp_addr(const sockaddr_storage_t *sa, sctpParam_t p)
+/* Convert a sockaddr_in to an IP address in an SCTP param.
+ * Returns len if a valid conversion was possible.
+ */
+int sockaddr2sctp_addr(const sockaddr_storage_t *sa, sctp_addr_param_t *p)
{
int len = 0;
switch (sa->v4.sin_family) {
case AF_INET:
- p.p->type = SCTP_PARAM_IPV4_ADDRESS;
- p.p->length = ntohs(sizeof(sctp_ipv4addr_param_t));
+ p->v4.param_hdr.type = SCTP_PARAM_IPV4_ADDRESS;
+ p->v4.param_hdr.length = ntohs(sizeof(sctp_ipv4addr_param_t));
len = sizeof(sctp_ipv4addr_param_t);
- p.v4->addr.s_addr = sa->v4.sin_addr.s_addr;
+ p->v4.addr.s_addr = sa->v4.sin_addr.s_addr;
break;
case AF_INET6:
- p.p->type = SCTP_PARAM_IPV6_ADDRESS;
- p.p->length = ntohs(sizeof(sctp_ipv6addr_param_t));
+ p->v6.param_hdr.type = SCTP_PARAM_IPV6_ADDRESS;
+ p->v6.param_hdr.length = ntohs(sizeof(sctp_ipv6addr_param_t));
len = sizeof(sctp_ipv6addr_param_t);
- p.v6->addr = *(&sa->v6.sin6_addr);
+ p->v6.addr = *(&sa->v6.sin6_addr);
break;
default:
diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c
index 4d8237779b67..5bb23c37a5fb 100644
--- a/net/sctp/sm_sideeffect.c
+++ b/net/sctp/sm_sideeffect.c
@@ -1118,7 +1118,8 @@ void sctp_cmd_set_bind_addrs(sctp_cmd_seq_t *cmds, sctp_association_t *asoc,
}
/* Helper function to handle the reception of an HEARTBEAT ACK. */
-static void sctp_cmd_transport_on(sctp_cmd_seq_t *cmds, sctp_association_t *asoc,
+static void sctp_cmd_transport_on(sctp_cmd_seq_t *cmds,
+ sctp_association_t *asoc,
sctp_transport_t *t, sctp_chunk_t *chunk)
{
sctp_sender_hb_info_t *hbinfo;
@@ -1164,7 +1165,8 @@ static void sctp_cmd_transport_reset(sctp_cmd_seq_t *cmds,
}
/* Helper function to process the process SACK command. */
-static int sctp_cmd_process_sack(sctp_cmd_seq_t *cmds, sctp_association_t *asoc,
+static int sctp_cmd_process_sack(sctp_cmd_seq_t *cmds,
+ sctp_association_t *asoc,
sctp_sackhdr_t *sackh)
{
int err;
diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c
index dd5d1ab51120..436638e2fccc 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
@@ -873,6 +862,105 @@ sctp_disposition_t sctp_sf_backbeat_8_3(const sctp_endpoint_t *ep,
return SCTP_DISPOSITION_CONSUME;
}
+/* Helper function to send out an abort for the restart
+ * condition.
+ */
+static int sctp_sf_send_restart_abort(sockaddr_storage_t *ssa,
+ sctp_chunk_t *init,
+ sctp_cmd_seq_t *commands)
+{
+ int len;
+ sctp_packet_t *pkt;
+ sctp_addr_param_t *addrparm;
+ sctp_errhdr_t *errhdr;
+ sctp_endpoint_t *ep;
+ char buffer[sizeof(sctp_errhdr_t) + sizeof(sctp_addr_param_t)];
+
+ /* Build the error on the stack. We are way to malloc
+ * malloc crazy throughout the code today.
+ */
+ errhdr = (sctp_errhdr_t *)buffer;
+ addrparm = (sctp_addr_param_t *)errhdr->variable;
+
+ /* Copy into a parm format. */
+ len = sockaddr2sctp_addr(ssa, addrparm);
+ len += sizeof(sctp_errhdr_t);
+
+ errhdr->cause = SCTP_ERROR_RESTART;
+ errhdr->length = htons(len);
+
+ /* Assign to the control socket. */
+ ep = sctp_sk((sctp_get_ctl_sock()))->ep;
+
+ /* Association is NULL since this may be a restart attack and we
+ * want to send back the attacker's vtag.
+ */
+ pkt = sctp_abort_pkt_new(ep, NULL, init, errhdr, len);
+
+ if (!pkt)
+ goto out;
+ sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT, SCTP_PACKET(pkt));
+
+ /* Discard the rest of the inbound packet. */
+ sctp_add_cmd_sf(commands, SCTP_CMD_DISCARD_PACKET, SCTP_NULL());
+
+out:
+ /* Even if there is no memory, treat as a failure so
+ * the packet will get dropped.
+ */
+ return 0;
+}
+
+/* A restart is occuring, check to make sure no new addresses
+ * are being added as we may be under a takeover attack.
+ */
+static int sctp_sf_check_restart_addrs(const sctp_association_t *new_asoc,
+ const sctp_association_t *asoc,
+ sctp_chunk_t *init,
+ sctp_cmd_seq_t *commands)
+{
+ sctp_transport_t *new_addr, *addr;
+ struct list_head *pos, *pos2;
+ int found;
+
+ /* Implementor's Guide - Sectin 5.2.2
+ * ...
+ * Before responding the endpoint MUST check to see if the
+ * unexpected INIT adds new addresses to the association. If new
+ * addresses are added to the association, the endpoint MUST respond
+ * with an ABORT..
+ */
+
+ /* Search through all current addresses and make sure
+ * we aren't adding any new ones.
+ */
+ new_addr = 0;
+ found = 0;
+
+ list_for_each(pos, &new_asoc->peer.transport_addr_list) {
+ new_addr = list_entry(pos, sctp_transport_t, transports);
+ found = 0;
+ list_for_each(pos2, &asoc->peer.transport_addr_list) {
+ addr = list_entry(pos2, sctp_transport_t, transports);
+ if (sctp_cmp_addr_exact(&new_addr->ipaddr,
+ &addr->ipaddr)) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found)
+ break;
+ }
+
+ /* If a new address was added, ABORT the sender. */
+ if (!found && new_addr) {
+ sctp_sf_send_restart_abort(&new_addr->ipaddr, init, commands);
+ }
+
+ /* Return success if all addresses were found. */
+ return found;
+}
+
/* Populate the verification/tie tags based on overlapping INIT
* scenario.
*
@@ -969,6 +1057,7 @@ static sctp_disposition_t sctp_sf_do_unexpected_init(
const sctp_subtype_t type,
void *arg, sctp_cmd_seq_t *commands)
{
+ sctp_disposition_t retval;
sctp_chunk_t *chunk = arg;
sctp_chunk_t *repl;
sctp_association_t *new_asoc;
@@ -1006,15 +1095,14 @@ static sctp_disposition_t sctp_sf_do_unexpected_init(
ntohs(err_chunk->chunk_hdr->length) -
sizeof(sctp_chunkhdr_t));
- sctp_free_chunk(err_chunk);
-
if (packet) {
sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT,
SCTP_PACKET(packet));
- return SCTP_DISPOSITION_CONSUME;
+ retval = SCTP_DISPOSITION_CONSUME;
} else {
- return SCTP_DISPOSITION_NOMEM;
+ retval = SCTP_DISPOSITION_NOMEM;
}
+ goto cleanup;
} else {
return sctp_sf_tabort_8_4_8(ep, asoc, type, arg,
commands);
@@ -1039,6 +1127,19 @@ static sctp_disposition_t sctp_sf_do_unexpected_init(
sctp_process_init(new_asoc, chunk->chunk_hdr->type, sctp_source(chunk),
(sctp_init_chunk_t *)chunk->chunk_hdr, GFP_ATOMIC);
+ /* Make sure no new addresses are being added during the
+ * restart. Do not do this check for COOKIE-WAIT state,
+ * since there are no peer addresses to check against.
+ * Upon return an ABORT will have been sent if needed.
+ */
+ if (asoc->state != SCTP_STATE_COOKIE_WAIT) {
+ if (!sctp_sf_check_restart_addrs(new_asoc, asoc, chunk,
+ commands)) {
+ retval = SCTP_DISPOSITION_CONSUME;
+ goto cleanup_asoc;
+ }
+ }
+
sctp_tietags_populate(new_asoc, asoc);
/* B) "Z" shall respond immediately with an INIT ACK chunk. */
@@ -1086,13 +1187,18 @@ static sctp_disposition_t sctp_sf_do_unexpected_init(
* Otherwise, "Z" will be vulnerable to resource attacks.
*/
sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, SCTP_NULL());
- return SCTP_DISPOSITION_CONSUME;
+ retval = SCTP_DISPOSITION_CONSUME;
-nomem:
+cleanup:
if (err_chunk)
sctp_free_chunk(err_chunk);
-
- return SCTP_DISPOSITION_NOMEM;
+ return retval;
+nomem:
+ retval = SCTP_DISPOSITION_NOMEM;
+ goto cleanup;
+cleanup_asoc:
+ sctp_association_free(new_asoc);
+ goto cleanup;
}
/*
@@ -1198,6 +1304,8 @@ sctp_disposition_t sctp_sf_do_5_2_2_dupinit(const sctp_endpoint_t *ep,
return sctp_sf_do_unexpected_init(ep, asoc, type, arg, commands);
}
+
+
/* Unexpected COOKIE-ECHO handlerfor peer restart (Table 2, action 'A')
*
* Section 5.2.4
@@ -1212,9 +1320,6 @@ static sctp_disposition_t sctp_sf_do_dupcook_a(const sctp_endpoint_t *ep,
sctp_init_chunk_t *peer_init;
sctp_ulpevent_t *ev;
sctp_chunk_t *repl;
- sctp_transport_t *new_addr, *addr;
- struct list_head *pos, *pos2, *temp;
- int found, error;
/* new_asoc is a brand-new association, so these are not yet
* side effects--it is safe to run them here.
@@ -1223,57 +1328,14 @@ static sctp_disposition_t sctp_sf_do_dupcook_a(const sctp_endpoint_t *ep,
sctp_process_init(new_asoc, chunk->chunk_hdr->type,
sctp_source(chunk), peer_init, GFP_ATOMIC);
- /* Make sure peer is not adding new addresses. */
- found = 0;
- new_addr = NULL;
- list_for_each(pos, &new_asoc->peer.transport_addr_list) {
- new_addr = list_entry(pos, sctp_transport_t, transports);
- found = 1;
- list_for_each_safe(pos2, temp,
- &asoc->peer.transport_addr_list) {
- addr = list_entry(pos2, sctp_transport_t, transports);
- if (!sctp_cmp_addr_exact(&new_addr->ipaddr,
- &addr->ipaddr)) {
- found = 0;
- break;
- }
- }
- if (!found)
- break;
- }
-
- if (!found) {
- sctp_bind_addr_t *bp;
- sctpParam_t rawaddr;
- int len;
-
- bp = sctp_bind_addr_new(GFP_ATOMIC);
- if (!bp)
- goto nomem;
-
- error = sctp_add_bind_addr(bp, &new_addr->ipaddr, GFP_ATOMIC);
- if (error)
- goto nomem_add;
-
- rawaddr = sctp_bind_addrs_to_raw(bp, &len, GFP_ATOMIC);
- if (!rawaddr.v)
- goto nomem_raw;
-
- repl = sctp_make_abort(asoc, chunk, len+sizeof(sctp_errhdr_t));
- if (!repl)
- goto nomem_abort;
- sctp_init_cause(repl, SCTP_ERROR_RESTART, rawaddr.v, len);
- sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(repl));
+ /* Make sure no new addresses are being added during the
+ * restart. Though this is a pretty complicated attack
+ * since you'd have to get inside the cookie.
+ */
+ if (!sctp_sf_check_restart_addrs(new_asoc, asoc, chunk, commands)) {
+ printk("cookie echo check\n");
return SCTP_DISPOSITION_CONSUME;
-
- nomem_abort:
- kfree(rawaddr.v);
-
- nomem_raw:
- nomem_add:
- sctp_bind_addr_free(bp);
- goto nomem;
- }
+ }
/* For now, fail any unsent/unacked data. Consider the optional
* choice of resending of this data.
@@ -1302,7 +1364,6 @@ static sctp_disposition_t sctp_sf_do_dupcook_a(const sctp_endpoint_t *ep,
nomem_ev:
sctp_free_chunk(repl);
-
nomem:
return SCTP_DISPOSITION_NOMEM;
}
@@ -1564,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,
@@ -1583,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,
@@ -1754,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... */
@@ -1772,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));
@@ -2526,7 +2602,7 @@ sctp_disposition_t sctp_sf_tabort_8_4_8(const sctp_endpoint_t *ep,
if (packet) {
/* Make an ABORT. The T bit will be set if the asoc
* is NULL.
- */
+ */
abort = sctp_make_abort(asoc, chunk, 0);
if (!abort) {
sctp_ootb_pkt_free(packet);
@@ -2699,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)
*
@@ -2751,6 +2820,31 @@ sctp_disposition_t sctp_sf_shut_8_4_5(const sctp_endpoint_t *ep,
}
/*
+ * 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.
*
* Section: 3.2. Also, 2.1 in the implementor's guide.
@@ -2881,12 +2975,11 @@ sctp_disposition_t sctp_sf_discard_chunk(const sctp_endpoint_t *ep,
*
* The return value is the disposition of the chunk.
*/
-sctp_disposition_t
-sctp_sf_pdiscard(const sctp_endpoint_t *ep,
- const sctp_association_t *asoc,
- const sctp_subtype_t type,
- void *arg,
- sctp_cmd_seq_t *commands)
+sctp_disposition_t sctp_sf_pdiscard(const sctp_endpoint_t *ep,
+ const sctp_association_t *asoc,
+ const sctp_subtype_t type,
+ void *arg,
+ sctp_cmd_seq_t *commands)
{
sctp_add_cmd_sf(commands, SCTP_CMD_DISCARD_PACKET, SCTP_NULL());
@@ -4090,10 +4183,10 @@ sctp_sackhdr_t *sctp_sm_pull_sack(sctp_chunk_t *chunk)
* error causes.
*/
sctp_packet_t *sctp_abort_pkt_new(const sctp_endpoint_t *ep,
- const sctp_association_t *asoc,
- sctp_chunk_t *chunk,
- const void *payload,
- size_t paylen)
+ const sctp_association_t *asoc,
+ sctp_chunk_t *chunk,
+ const void *payload,
+ size_t paylen)
{
sctp_packet_t *packet;
sctp_chunk_t *abort;
@@ -4126,7 +4219,7 @@ sctp_packet_t *sctp_abort_pkt_new(const sctp_endpoint_t *ep,
/* Allocate a packet for responding in the OOTB conditions. */
sctp_packet_t *sctp_ootb_pkt_new(const sctp_association_t *asoc,
- const sctp_chunk_t *chunk)
+ const sctp_chunk_t *chunk)
{
sctp_packet_t *packet;
sctp_transport_t *transport;
@@ -4144,7 +4237,14 @@ sctp_packet_t *sctp_ootb_pkt_new(const sctp_association_t *asoc,
if (asoc) {
vtag = asoc->peer.i.init_tag;
} else {
- vtag = ntohl(chunk->sctp_hdr->vtag);
+ /* 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;
+ vtag = ntohl(init->init_hdr.init_tag);
+ } else {
+ vtag = ntohl(chunk->sctp_hdr->vtag);
+ }
}
/* Make a transport for the bucket, Eliza... */
diff --git a/net/sctp/sm_statetable.c b/net/sctp/sm_statetable.c
index 3190642ee411..8d61ad763f83 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 */ \
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index 94705b026914..7e2982f1d648 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -726,9 +726,9 @@ SCTP_STATIC void sctp_close(struct sock *sk, long timeout)
* flags - flags sent or received with the user message, see Section
* 5 for complete description of the flags.
*
- * NB: The argument 'msg' is a user space address.
+ * Note: This function could use a rewrite especially when explicit
+ * connect support comes in.
*/
-/* BUG: We do not implement timeouts. */
/* BUG: We do not implement the equivalent of wait_for_tcp_memory(). */
SCTP_STATIC int sctp_msghdr_parse(const struct msghdr *, sctp_cmsgs_t *);
@@ -738,7 +738,7 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
{
sctp_opt_t *sp;
sctp_endpoint_t *ep;
- sctp_association_t *asoc = NULL;
+ sctp_association_t *new_asoc=NULL, *asoc=NULL;
sctp_transport_t *transport;
sctp_chunk_t *chunk = NULL;
sockaddr_storage_t to;
@@ -821,7 +821,32 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
/* 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.
@@ -907,11 +932,12 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
}
scope = sctp_scope(&to);
- asoc = sctp_association_new(ep, sk, scope, GFP_KERNEL);
- if (!asoc) {
+ new_asoc = sctp_association_new(ep, sk, scope, GFP_KERNEL);
+ if (!new_asoc) {
err = -ENOMEM;
goto out_unlock;
}
+ asoc = new_asoc;
/* If the SCTP_INIT ancillary data is specified, set all
* the association init values accordingly.
@@ -946,7 +972,7 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
}
/* ASSERT: we have a valid association at this point. */
- SCTP_DEBUG_PRINTK("We have a valid association. \n");
+ SCTP_DEBUG_PRINTK("We have a valid association.\n");
/* API 7.1.7, the sndbuf size per association bounds the
* maximum size of data that can be sent in a single send call.
@@ -1054,10 +1080,16 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
err = msg_len;
goto out_unlock;
}
+ /* If we are already past ASSOCIATE, the lower
+ * layers are responsible for its cleanup.
+ */
+ goto out_free_chunk;
out_free:
- if (SCTP_STATE_CLOSED == asoc->state)
+ if (new_asoc)
sctp_association_free(asoc);
+
+out_free_chunk:
if (chunk)
sctp_free_chunk(chunk);
@@ -1577,6 +1609,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
@@ -1606,6 +1640,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.
*/
@@ -1623,39 +1668,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,