diff options
| -rw-r--r-- | include/net/sctp/sm.h | 37 | ||||
| -rw-r--r-- | include/net/sctp/structs.h | 8 | ||||
| -rw-r--r-- | net/sctp/associola.c | 8 | ||||
| -rw-r--r-- | net/sctp/bind_addr.c | 32 | ||||
| -rw-r--r-- | net/sctp/input.c | 65 | ||||
| -rw-r--r-- | net/sctp/output.c | 14 | ||||
| -rw-r--r-- | net/sctp/outqueue.c | 163 | ||||
| -rw-r--r-- | net/sctp/sm_make_chunk.c | 67 | ||||
| -rw-r--r-- | net/sctp/sm_sideeffect.c | 6 | ||||
| -rw-r--r-- | net/sctp/sm_statefuns.c | 294 | ||||
| -rw-r--r-- | net/sctp/sm_statetable.c | 4 | ||||
| -rw-r--r-- | net/sctp/socket.c | 92 |
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, |
