Commit a9d077d8 authored by David S. Miller's avatar David S. Miller

Merge nuts.ninka.net:/home/davem/src/BK/sctp-2.5

into nuts.ninka.net:/home/davem/src/BK/net-2.5
parents c3e626e1 b18b05fb
...@@ -139,6 +139,7 @@ sctp_state_fn_t sctp_sf_do_5_2_1_siminit; ...@@ -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_2_dupinit;
sctp_state_fn_t sctp_sf_do_5_2_4_dupcook; 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_unk_chunk;
sctp_state_fn_t sctp_sf_do_8_5_1_E_sa;
/* Prototypes for primitive event state functions. */ /* Prototypes for primitive event state functions. */
sctp_state_fn_t sctp_sf_do_prm_asoc; sctp_state_fn_t sctp_sf_do_prm_asoc;
...@@ -329,10 +330,10 @@ __u32 sctp_generate_tag(const sctp_endpoint_t *); ...@@ -329,10 +330,10 @@ __u32 sctp_generate_tag(const sctp_endpoint_t *);
__u32 sctp_generate_tsn(const sctp_endpoint_t *); __u32 sctp_generate_tsn(const sctp_endpoint_t *);
/* 4th level prototypes */ /* 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); __u16 port);
int sctp_addr2sockaddr(const sctpParam_t, sockaddr_storage_t *); 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. */ /* Extern declarations for major data structures. */
sctp_sm_table_entry_t *sctp_chunk_event_lookup(sctp_cid_t, sctp_state_t); 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 ...@@ -432,4 +433,36 @@ static inline void sctp_add_cmd_sf(sctp_cmd_seq_t *seq, sctp_verb_t verb, sctp_a
BUG(); 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__ */ #endif /* __sctp_sm_h__ */
...@@ -378,7 +378,7 @@ typedef union { ...@@ -378,7 +378,7 @@ typedef union {
typedef union { typedef union {
sctp_ipv4addr_param_t v4; sctp_ipv4addr_param_t v4;
sctp_ipv6addr_param_t v6; sctp_ipv6addr_param_t v6;
} sctpIpAddress_t; } sctp_addr_param_t;
/* RFC 2960. Section 3.3.5 Heartbeat. /* RFC 2960. Section 3.3.5 Heartbeat.
* Heartbeat Information: variable length * Heartbeat Information: variable length
...@@ -1044,6 +1044,9 @@ sctp_association_t *sctp_endpoint_lookup_assoc(const sctp_endpoint_t *ep, ...@@ -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 *, sctp_endpoint_t *sctp_endpoint_is_match(sctp_endpoint_t *,
const sockaddr_storage_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, int sctp_verify_init(const sctp_association_t *asoc,
sctp_cid_t cid, sctp_cid_t cid,
sctp_init_chunk_t *peer_init, sctp_init_chunk_t *peer_init,
...@@ -1312,6 +1315,9 @@ struct SCTP_association { ...@@ -1312,6 +1315,9 @@ struct SCTP_association {
__u32 ctsn_ack_point; __u32 ctsn_ack_point;
/* Highest TSN that is acknowledged by incoming SACKs. */
__u32 highest_sacked;
/* The number of unacknowledged data chunks. Reported through /* The number of unacknowledged data chunks. Reported through
* the SCTP_STATUS sockopt. * the SCTP_STATUS sockopt.
*/ */
......
...@@ -207,6 +207,7 @@ sctp_association_t *sctp_association_init(sctp_association_t *asoc, ...@@ -207,6 +207,7 @@ sctp_association_t *sctp_association_init(sctp_association_t *asoc,
asoc->next_tsn = asoc->c.initial_tsn; asoc->next_tsn = asoc->c.initial_tsn;
asoc->ctsn_ack_point = asoc->next_tsn - 1; asoc->ctsn_ack_point = asoc->next_tsn - 1;
asoc->highest_sacked = asoc->ctsn_ack_point;
asoc->unack_data = 0; asoc->unack_data = 0;
...@@ -476,13 +477,6 @@ sctp_transport_t *sctp_assoc_add_peer(sctp_association_t *asoc, ...@@ -476,13 +477,6 @@ sctp_transport_t *sctp_assoc_add_peer(sctp_association_t *asoc,
asoc->peer.retran_path = peer; 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) if (asoc->peer.active_path == asoc->peer.retran_path)
asoc->peer.retran_path = peer; asoc->peer.retran_path = peer;
......
...@@ -199,11 +199,10 @@ int sctp_del_bind_addr(sctp_bind_addr_t *bp, sockaddr_storage_t *del_addr) ...@@ -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, sctpParam_t sctp_bind_addrs_to_raw(const sctp_bind_addr_t *bp, int *addrs_len,
int priority) int priority)
{ {
sctpParam_t rawaddr;
sctpParam_t addrparms; sctpParam_t addrparms;
sctpParam_t retval; sctpParam_t retval;
int addrparms_len; int addrparms_len;
sctpIpAddress_t rawaddr_space; sctp_addr_param_t rawaddr;
int len; int len;
struct sockaddr_storage_list *addr; struct sockaddr_storage_list *addr;
struct list_head *pos; struct list_head *pos;
...@@ -214,7 +213,7 @@ sctpParam_t sctp_bind_addrs_to_raw(const sctp_bind_addr_t *bp, int *addrs_len, ...@@ -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. */ /* Allocate enough memory at once. */
list_for_each(pos, &bp->address_list) { list_for_each(pos, &bp->address_list) {
len += sizeof(sctp_ipv6addr_param_t); len += sizeof(sctp_addr_param_t);
} }
addrparms.v = kmalloc(len, priority); 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, ...@@ -222,12 +221,11 @@ sctpParam_t sctp_bind_addrs_to_raw(const sctp_bind_addr_t *bp, int *addrs_len,
goto end_raw; goto end_raw;
retval = addrparms; retval = addrparms;
rawaddr.v4 = &rawaddr_space.v4;
list_for_each(pos, &bp->address_list) { list_for_each(pos, &bp->address_list) {
addr = list_entry(pos, struct sockaddr_storage_list, list); addr = list_entry(pos, struct sockaddr_storage_list, list);
len = sockaddr2sctp_addr(&addr->a, rawaddr); len = sockaddr2sctp_addr(&addr->a, &rawaddr);
memcpy(addrparms.v, rawaddr.v, len); memcpy(addrparms.v, &rawaddr, len);
addrparms.v += len; addrparms.v += len;
addrparms_len += len; addrparms_len += len;
} }
...@@ -244,33 +242,39 @@ sctpParam_t sctp_bind_addrs_to_raw(const sctp_bind_addr_t *bp, int *addrs_len, ...@@ -244,33 +242,39 @@ sctpParam_t sctp_bind_addrs_to_raw(const sctp_bind_addr_t *bp, int *addrs_len,
int sctp_raw_to_bind_addrs(sctp_bind_addr_t *bp, __u8 *raw_addr_list, int sctp_raw_to_bind_addrs(sctp_bind_addr_t *bp, __u8 *raw_addr_list,
int addrs_len, __u16 port, int priority) int addrs_len, __u16 port, int priority)
{ {
sctpParam_t rawaddr; sctp_addr_param_t *rawaddr;
sctp_paramhdr_t *param;
sockaddr_storage_t addr; sockaddr_storage_t addr;
int retval = 0; int retval = 0;
int len; int len;
/* Convert the raw address to standard address format */ /* Convert the raw address to standard address format */
while (addrs_len) { while (addrs_len) {
rawaddr.v = raw_addr_list; param = (sctp_paramhdr_t *)raw_addr_list;
if (SCTP_PARAM_IPV4_ADDRESS==rawaddr.p->type rawaddr = (sctp_addr_param_t *)raw_addr_list;
|| SCTP_PARAM_IPV6_ADDRESS==rawaddr.p->type) {
switch (param->type) {
case SCTP_PARAM_IPV4_ADDRESS:
case SCTP_PARAM_IPV6_ADDRESS:
sctp_param2sockaddr(&addr, rawaddr, port); sctp_param2sockaddr(&addr, rawaddr, port);
retval = sctp_add_bind_addr(bp, &addr, priority); retval = sctp_add_bind_addr(bp, &addr, priority);
if (retval) { if (retval) {
/* Can't finish building the list, clean up. */ /* Can't finish building the list, clean up. */
sctp_bind_addr_clean(bp); sctp_bind_addr_clean(bp);
break; break;;
} }
len = ntohs(param->length);
len = ntohs(rawaddr.p->length);
addrs_len -= len; addrs_len -= len;
raw_addr_list += len; raw_addr_list += len;
} else { break;
default:
/* Corrupted raw addr list! */ /* Corrupted raw addr list! */
retval = -EINVAL; retval = -EINVAL;
sctp_bind_addr_clean(bp); sctp_bind_addr_clean(bp);
break; break;
} }
if (retval)
break;
} }
return retval; return retval;
......
...@@ -522,7 +522,7 @@ void __sctp_unhash_established(sctp_association_t *asoc) ...@@ -522,7 +522,7 @@ void __sctp_unhash_established(sctp_association_t *asoc)
} }
/* Look up an association. */ /* Look up an association. */
sctp_association_t *__sctp_rcv_lookup_association(const sockaddr_storage_t *laddr, sctp_association_t *__sctp_lookup_association(const sockaddr_storage_t *laddr,
const sockaddr_storage_t *paddr, const sockaddr_storage_t *paddr,
sctp_transport_t **transportp) sctp_transport_t **transportp)
{ {
...@@ -557,6 +557,36 @@ sctp_association_t *__sctp_rcv_lookup_association(const sockaddr_storage_t *ladd ...@@ -557,6 +557,36 @@ sctp_association_t *__sctp_rcv_lookup_association(const sockaddr_storage_t *ladd
return asoc; 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 * SCTP Implementors Guide, 2.18 Handling of address
* parameters within the INIT or INIT-ACK. * parameters within the INIT or INIT-ACK.
...@@ -584,14 +614,20 @@ static sctp_association_t *__sctp_rcv_initack_lookup(struct sk_buff *skb, ...@@ -584,14 +614,20 @@ static sctp_association_t *__sctp_rcv_initack_lookup(struct sk_buff *skb,
struct sctphdr *sh = (struct sctphdr *) skb->h.raw; struct sctphdr *sh = (struct sctphdr *) skb->h.raw;
sctp_chunkhdr_t *ch; sctp_chunkhdr_t *ch;
__u8 *ch_end, *data; __u8 *ch_end, *data;
sctpParam_t parm; sctp_paramhdr_t *parm;
ch = (sctp_chunkhdr_t *) skb->data; ch = (sctp_chunkhdr_t *) skb->data;
ch_end = ((__u8 *) ch) + WORD_ROUND(ntohs(ch->length)); 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; return NULL;
}
/* /*
* This code will NOT touch anything inside the chunk--it is * 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, ...@@ -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 /* Find the start of the TLVs and the end of the chunk. This is
* the region we search for address parameters. * the region we search for address parameters.
*/ */
data = skb->data + sizeof(sctp_init_chunk_t); data = skb->data + sizeof(sctp_init_chunk_t);
/* See sctp_process_init() for how to go thru TLVs. */ /* See sctp_process_init() for how to go thru TLVs. */
while (data < ch_end) { while (data < ch_end) {
parm.v = data; parm = (sctp_paramhdr_t *)data;
if (!parm.p->length) if (!parm->length)
break; break;
data += WORD_ROUND(ntohs(parm.p->length)); data += WORD_ROUND(ntohs(parm->length));
/* Note: Ignoring hostname addresses. */ /* Note: Ignoring hostname addresses. */
if ((SCTP_PARAM_IPV4_ADDRESS != parm.p->type) && if ((SCTP_PARAM_IPV4_ADDRESS != parm->type) &&
(SCTP_PARAM_IPV6_ADDRESS != parm.p->type)) (SCTP_PARAM_IPV6_ADDRESS != parm->type))
continue; continue;
sctp_param2sockaddr(paddr, parm, ntohs(sh->source)); sctp_param2sockaddr(paddr, (sctp_addr_param_t *)parm,
ntohs(sh->source));
asoc = __sctp_rcv_lookup_association(laddr, paddr, transportp); asoc = __sctp_lookup_association(laddr, paddr, transportp);
if (asoc) if (asoc)
return asoc; return asoc;
} }
...@@ -644,7 +679,7 @@ sctp_association_t *__sctp_rcv_lookup(struct sk_buff *skb, ...@@ -644,7 +679,7 @@ sctp_association_t *__sctp_rcv_lookup(struct sk_buff *skb,
{ {
sctp_association_t *asoc; 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. /* Further lookup for INIT-ACK packet.
* SCTP Implementors Guide, 2.18 Handling of address * SCTP Implementors Guide, 2.18 Handling of address
......
...@@ -530,10 +530,18 @@ static sctp_xmit_t sctp_packet_append_data(sctp_packet_t *packet, ...@@ -530,10 +530,18 @@ static sctp_xmit_t sctp_packet_append_data(sctp_packet_t *packet,
* to a given transport address if it has cwnd or more bytes * to a given transport address if it has cwnd or more bytes
* of data outstanding to that transport address. * of data outstanding to that transport address.
*/ */
/* 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) { if (transport->flight_size >= transport->cwnd) {
retval = SCTP_XMIT_RWND_FULL; retval = SCTP_XMIT_RWND_FULL;
goto finish; goto finish;
} }
}
/* Keep track of how many bytes are in flight over this transport. */ /* Keep track of how many bytes are in flight over this transport. */
transport->flight_size += datasize; transport->flight_size += datasize;
......
...@@ -59,7 +59,8 @@ static int sctp_acked(sctp_sackhdr_t *sack, __u32 tsn); ...@@ -59,7 +59,8 @@ static int sctp_acked(sctp_sackhdr_t *sack, __u32 tsn);
static void sctp_check_transmitted(sctp_outqueue_t *q, static void sctp_check_transmitted(sctp_outqueue_t *q,
struct list_head *transmitted_queue, struct list_head *transmitted_queue,
sctp_transport_t *transport, sctp_transport_t *transport,
sctp_sackhdr_t *sack); sctp_sackhdr_t *sack,
__u32 highest_new_tsn);
/* Generate a new outqueue. */ /* Generate a new outqueue. */
sctp_outqueue_t *sctp_outqueue_new(sctp_association_t *asoc) sctp_outqueue_t *sctp_outqueue_new(sctp_association_t *asoc)
...@@ -773,6 +774,12 @@ int sctp_flush_outqueue(sctp_outqueue_t *q, int rtx_timeout) ...@@ -773,6 +774,12 @@ int sctp_flush_outqueue(sctp_outqueue_t *q, int rtx_timeout)
*/ */
if (packet->has_cookie_echo) if (packet->has_cookie_echo)
goto sctp_flush_out; 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. */ /* Finally, transmit new packets. */
...@@ -849,7 +856,7 @@ int sctp_flush_outqueue(sctp_outqueue_t *q, int rtx_timeout) ...@@ -849,7 +856,7 @@ int sctp_flush_outqueue(sctp_outqueue_t *q, int rtx_timeout)
/* We could not append this chunk, so put /* We could not append this chunk, so put
* the chunk back on the output queue. * 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", "not transmit TSN: 0x%x, status: %d\n",
ntohl(chunk->subh.data_hdr->tsn), ntohl(chunk->subh.data_hdr->tsn),
status); status);
...@@ -971,6 +978,36 @@ static void sctp_sack_update_unack_data(sctp_association_t *assoc, ...@@ -971,6 +978,36 @@ static void sctp_sack_update_unack_data(sctp_association_t *assoc,
assoc->unack_data = unack_data; 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. /* This is where we REALLY process a SACK.
* *
* Process the sack against the outqueue. Mostly, this just frees * 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, ...@@ -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) 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; sctp_chunk_t *tchunk;
struct list_head *lchunk, *transport_list, *pos; struct list_head *lchunk, *transport_list, *pos;
__u32 tsn; sctp_sack_variable_t *frags = sack->variable;
__u32 sack_ctsn; __u32 sack_ctsn, ctsn, tsn;
__u32 ctsn; __u32 highest_tsn, highest_new_tsn;
sctp_transport_t *transport;
int outstanding;
__u32 sack_a_rwnd; __u32 sack_a_rwnd;
int outstanding;
/* Grab the association's destination address list. */ /* 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 /* Run through the retransmit queue. Credit bytes received
* and free those chunks that we can. * 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. /* Run through the transmitted queue.
* Credit bytes received and free those chunks which we can. * 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) ...@@ -1003,23 +1054,22 @@ int sctp_sack_outqueue(sctp_outqueue_t *q, sctp_sackhdr_t *sack)
list_for_each(pos, transport_list) { list_for_each(pos, transport_list) {
transport = list_entry(pos, sctp_transport_t, transports); transport = list_entry(pos, sctp_transport_t, transports);
sctp_check_transmitted(q, &transport->transmitted, sctp_check_transmitted(q, &transport->transmitted,
transport, sack); transport, sack, highest_new_tsn);
} }
/* Move the Cumulative TSN Ack Point if appropriate. */ /* Move the Cumulative TSN Ack Point if appropriate. */
sack_ctsn = ntohl(sack->cum_tsn_ack); if (TSN_lt(asoc->ctsn_ack_point, sack_ctsn))
if (TSN_lt(q->asoc->ctsn_ack_point, sack_ctsn)) asoc->ctsn_ack_point = sack_ctsn;
q->asoc->ctsn_ack_point = sack_ctsn;
/* Update unack_data field in the assoc. */ /* 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", SCTP_DEBUG_PRINTK("%s: sack Cumulative TSN Ack is 0x%x.\n",
__FUNCTION__, sack_ctsn); __FUNCTION__, sack_ctsn);
SCTP_DEBUG_PRINTK("%s: Cumulative TSN Ack of association " 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. */ /* Throw away stuff rotting on the sack queue. */
list_for_each(lchunk, &q->sacked) { list_for_each(lchunk, &q->sacked) {
...@@ -1045,7 +1095,7 @@ int sctp_sack_outqueue(sctp_outqueue_t *q, sctp_sackhdr_t *sack) ...@@ -1045,7 +1095,7 @@ int sctp_sack_outqueue(sctp_outqueue_t *q, sctp_sackhdr_t *sack)
sack_a_rwnd = 0; sack_a_rwnd = 0;
} }
q->asoc->peer.rwnd = sack_a_rwnd; asoc->peer.rwnd = sack_a_rwnd;
/* See if all chunks are acked. /* See if all chunks are acked.
* Make sure the empty queue handler will get run later. * Make sure the empty queue handler will get run later.
...@@ -1092,7 +1142,8 @@ int sctp_outqueue_is_empty(const sctp_outqueue_t *q) ...@@ -1092,7 +1142,8 @@ int sctp_outqueue_is_empty(const sctp_outqueue_t *q)
static void sctp_check_transmitted(sctp_outqueue_t *q, static void sctp_check_transmitted(sctp_outqueue_t *q,
struct list_head *transmitted_queue, struct list_head *transmitted_queue,
sctp_transport_t *transport, sctp_transport_t *transport,
sctp_sackhdr_t *sack) sctp_sackhdr_t *sack,
__u32 highest_new_tsn_in_sack)
{ {
struct list_head *lchunk; struct list_head *lchunk;
sctp_chunk_t *tchunk; sctp_chunk_t *tchunk;
...@@ -1100,7 +1151,6 @@ static void sctp_check_transmitted(sctp_outqueue_t *q, ...@@ -1100,7 +1151,6 @@ static void sctp_check_transmitted(sctp_outqueue_t *q,
__u32 tsn; __u32 tsn;
__u32 sack_ctsn; __u32 sack_ctsn;
__u32 rtt; __u32 rtt;
__u32 highest_new_tsn_in_sack;
__u8 restart_timer = 0; __u8 restart_timer = 0;
__u8 do_fast_retransmit = 0; __u8 do_fast_retransmit = 0;
int bytes_acked = 0; int bytes_acked = 0;
...@@ -1121,7 +1171,6 @@ static void sctp_check_transmitted(sctp_outqueue_t *q, ...@@ -1121,7 +1171,6 @@ static void sctp_check_transmitted(sctp_outqueue_t *q,
#endif /* SCTP_DEBUG */ #endif /* SCTP_DEBUG */
sack_ctsn = ntohl(sack->cum_tsn_ack); sack_ctsn = ntohl(sack->cum_tsn_ack);
highest_new_tsn_in_sack = sack_ctsn;
INIT_LIST_HEAD(&tlist); INIT_LIST_HEAD(&tlist);
...@@ -1194,10 +1243,6 @@ static void sctp_check_transmitted(sctp_outqueue_t *q, ...@@ -1194,10 +1243,6 @@ static void sctp_check_transmitted(sctp_outqueue_t *q,
if (!tchunk->tsn_gap_acked) { if (!tchunk->tsn_gap_acked) {
tchunk->tsn_gap_acked = 1; tchunk->tsn_gap_acked = 1;
bytes_acked += sctp_data_size(tchunk); 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); list_add_tail(lchunk, &tlist);
} }
......
...@@ -1710,6 +1710,7 @@ int sctp_process_param(sctp_association_t *asoc, sctpParam_t param, ...@@ -1710,6 +1710,7 @@ int sctp_process_param(sctp_association_t *asoc, sctpParam_t param,
sctp_cid_t cid, int priority) sctp_cid_t cid, int priority)
{ {
sockaddr_storage_t addr; sockaddr_storage_t addr;
sctp_addr_param_t *addrparm;
int j; int j;
int i; int i;
int retval = 1; int retval = 1;
...@@ -1721,24 +1722,23 @@ int sctp_process_param(sctp_association_t *asoc, sctpParam_t param, ...@@ -1721,24 +1722,23 @@ int sctp_process_param(sctp_association_t *asoc, sctpParam_t param,
*/ */
switch (param.p->type) { switch (param.p->type) {
case SCTP_PARAM_IPV4_ADDRESS: case SCTP_PARAM_IPV4_ADDRESS:
if (SCTP_CID_INIT != cid) { addrparm = (sctp_addr_param_t *)param.v;
sctp_param2sockaddr(&addr, param, asoc->peer.port); sctp_param2sockaddr(&addr, addrparm, asoc->peer.port);
scope = sctp_scope(peer_addr); scope = sctp_scope(peer_addr);
if (sctp_in_scope(&addr, scope)) if (sctp_in_scope(&addr, scope))
sctp_assoc_add_peer(asoc, &addr, priority); sctp_assoc_add_peer(asoc, &addr, priority);
}
break; break;
case SCTP_PARAM_IPV6_ADDRESS: case SCTP_PARAM_IPV6_ADDRESS:
if (SCTP_CID_INIT != cid) { /* Rethink this as we may need to keep for
* restart considerations.
*/
if (PF_INET6 == asoc->base.sk->family) { if (PF_INET6 == asoc->base.sk->family) {
sctp_param2sockaddr(&addr, param, addrparm = (sctp_addr_param_t *)param.v;
asoc->peer.port); sctp_param2sockaddr(&addr, addrparm, asoc->peer.port);
scope = sctp_scope(peer_addr); scope = sctp_scope(peer_addr);
if (sctp_in_scope(&addr, scope)) if (sctp_in_scope(&addr, scope))
sctp_assoc_add_peer(asoc, &addr, sctp_assoc_add_peer(asoc, &addr, priority);
priority);
}
} }
break; break;
...@@ -1833,7 +1833,6 @@ __u32 sctp_generate_tag(const sctp_endpoint_t *ep) ...@@ -1833,7 +1833,6 @@ __u32 sctp_generate_tag(const sctp_endpoint_t *ep)
/* Select an initial TSN to send during startup. */ /* Select an initial TSN to send during startup. */
__u32 sctp_generate_tsn(const sctp_endpoint_t *ep) __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)); get_random_bytes(&retval, sizeof(__u32));
...@@ -1845,26 +1844,27 @@ __u32 sctp_generate_tsn(const sctp_endpoint_t *ep) ...@@ -1845,26 +1844,27 @@ __u32 sctp_generate_tsn(const sctp_endpoint_t *ep)
********************************************************************/ ********************************************************************/
/* Convert from an SCTP IP parameter to a sockaddr_storage_t. */ /* 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: case SCTP_PARAM_IPV4_ADDRESS:
addr->v4.sin_family = AF_INET; addr->v4.sin_family = AF_INET;
addr->v4.sin_port = port; 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; break;
case SCTP_PARAM_IPV6_ADDRESS: case SCTP_PARAM_IPV6_ADDRESS:
addr->v6.sin6_family = AF_INET6; addr->v6.sin6_family = AF_INET6;
addr->v6.sin6_port = port; addr->v6.sin6_port = port;
addr->v6.sin6_flowinfo = 0; /* BUG */ 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 */ addr->v6.sin6_scope_id = 0; /* BUG */
break; break;
default: default:
SCTP_DEBUG_PRINTK("Illegal address type %d\n", SCTP_DEBUG_PRINTK("Illegal address type %d\n",
ntohs(param.p->type)); ntohs(param->v4.param_hdr.type));
break; break;
}; };
} }
...@@ -1904,11 +1904,9 @@ int ipver2af(__u8 ipver) ...@@ -1904,11 +1904,9 @@ int ipver2af(__u8 ipver)
case 4: case 4:
family = AF_INET; family = AF_INET;
break; break;
case 6: case 6:
family = AF_INET6; family = AF_INET6;
break; break;
default: default:
family = 0; family = 0;
break; break;
...@@ -1917,25 +1915,26 @@ int ipver2af(__u8 ipver) ...@@ -1917,25 +1915,26 @@ int ipver2af(__u8 ipver)
return family; return family;
} }
/* Convert a sockaddr_in to IP address in an SCTP para. */ /* Convert a sockaddr_in to an IP address in an SCTP param.
/* Returns true if a valid conversion was possible. */ * Returns len if a valid conversion was possible.
int sockaddr2sctp_addr(const sockaddr_storage_t *sa, sctpParam_t p) */
int sockaddr2sctp_addr(const sockaddr_storage_t *sa, sctp_addr_param_t *p)
{ {
int len = 0; int len = 0;
switch (sa->v4.sin_family) { switch (sa->v4.sin_family) {
case AF_INET: case AF_INET:
p.p->type = SCTP_PARAM_IPV4_ADDRESS; p->v4.param_hdr.type = SCTP_PARAM_IPV4_ADDRESS;
p.p->length = ntohs(sizeof(sctp_ipv4addr_param_t)); p->v4.param_hdr.length = ntohs(sizeof(sctp_ipv4addr_param_t));
len = 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; break;
case AF_INET6: case AF_INET6:
p.p->type = SCTP_PARAM_IPV6_ADDRESS; p->v6.param_hdr.type = SCTP_PARAM_IPV6_ADDRESS;
p.p->length = ntohs(sizeof(sctp_ipv6addr_param_t)); p->v6.param_hdr.length = ntohs(sizeof(sctp_ipv6addr_param_t));
len = 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; break;
default: default:
......
...@@ -1118,7 +1118,8 @@ void sctp_cmd_set_bind_addrs(sctp_cmd_seq_t *cmds, sctp_association_t *asoc, ...@@ -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. */ /* 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_transport_t *t, sctp_chunk_t *chunk)
{ {
sctp_sender_hb_info_t *hbinfo; sctp_sender_hb_info_t *hbinfo;
...@@ -1164,7 +1165,8 @@ static void sctp_cmd_transport_reset(sctp_cmd_seq_t *cmds, ...@@ -1164,7 +1165,8 @@ static void sctp_cmd_transport_reset(sctp_cmd_seq_t *cmds,
} }
/* Helper function to process the process SACK command. */ /* 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) sctp_sackhdr_t *sackh)
{ {
int err; int err;
......
...@@ -110,18 +110,7 @@ sctp_disposition_t sctp_sf_do_4_C(const sctp_endpoint_t *ep, ...@@ -110,18 +110,7 @@ sctp_disposition_t sctp_sf_do_4_C(const sctp_endpoint_t *ep,
if (!chunk->singleton) if (!chunk->singleton)
return SCTP_DISPOSITION_VIOLATION; return SCTP_DISPOSITION_VIOLATION;
/* RFC 2960 8.5.1 Exceptions in Verification Tag Rules if (!sctp_vtag_verify_either(chunk, asoc))
*
* (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)))
return sctp_sf_pdiscard(ep, asoc, type, arg, commands); return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
/* RFC 2960 10.2 SCTP-to-ULP /* RFC 2960 10.2 SCTP-to-ULP
...@@ -873,6 +862,105 @@ sctp_disposition_t sctp_sf_backbeat_8_3(const sctp_endpoint_t *ep, ...@@ -873,6 +862,105 @@ sctp_disposition_t sctp_sf_backbeat_8_3(const sctp_endpoint_t *ep,
return SCTP_DISPOSITION_CONSUME; 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 /* Populate the verification/tie tags based on overlapping INIT
* scenario. * scenario.
* *
...@@ -969,6 +1057,7 @@ static sctp_disposition_t sctp_sf_do_unexpected_init( ...@@ -969,6 +1057,7 @@ static sctp_disposition_t sctp_sf_do_unexpected_init(
const sctp_subtype_t type, const sctp_subtype_t type,
void *arg, sctp_cmd_seq_t *commands) void *arg, sctp_cmd_seq_t *commands)
{ {
sctp_disposition_t retval;
sctp_chunk_t *chunk = arg; sctp_chunk_t *chunk = arg;
sctp_chunk_t *repl; sctp_chunk_t *repl;
sctp_association_t *new_asoc; sctp_association_t *new_asoc;
...@@ -1006,15 +1095,14 @@ static sctp_disposition_t sctp_sf_do_unexpected_init( ...@@ -1006,15 +1095,14 @@ static sctp_disposition_t sctp_sf_do_unexpected_init(
ntohs(err_chunk->chunk_hdr->length) - ntohs(err_chunk->chunk_hdr->length) -
sizeof(sctp_chunkhdr_t)); sizeof(sctp_chunkhdr_t));
sctp_free_chunk(err_chunk);
if (packet) { if (packet) {
sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT, sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT,
SCTP_PACKET(packet)); SCTP_PACKET(packet));
return SCTP_DISPOSITION_CONSUME; retval = SCTP_DISPOSITION_CONSUME;
} else { } else {
return SCTP_DISPOSITION_NOMEM; retval = SCTP_DISPOSITION_NOMEM;
} }
goto cleanup;
} else { } else {
return sctp_sf_tabort_8_4_8(ep, asoc, type, arg, return sctp_sf_tabort_8_4_8(ep, asoc, type, arg,
commands); commands);
...@@ -1039,6 +1127,19 @@ static sctp_disposition_t sctp_sf_do_unexpected_init( ...@@ -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_process_init(new_asoc, chunk->chunk_hdr->type, sctp_source(chunk),
(sctp_init_chunk_t *)chunk->chunk_hdr, GFP_ATOMIC); (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); sctp_tietags_populate(new_asoc, asoc);
/* B) "Z" shall respond immediately with an INIT ACK chunk. */ /* B) "Z" shall respond immediately with an INIT ACK chunk. */
...@@ -1086,13 +1187,18 @@ static sctp_disposition_t sctp_sf_do_unexpected_init( ...@@ -1086,13 +1187,18 @@ static sctp_disposition_t sctp_sf_do_unexpected_init(
* Otherwise, "Z" will be vulnerable to resource attacks. * Otherwise, "Z" will be vulnerable to resource attacks.
*/ */
sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, SCTP_NULL()); sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, SCTP_NULL());
return SCTP_DISPOSITION_CONSUME; retval = SCTP_DISPOSITION_CONSUME;
nomem: cleanup:
if (err_chunk) if (err_chunk)
sctp_free_chunk(err_chunk); sctp_free_chunk(err_chunk);
return retval;
return SCTP_DISPOSITION_NOMEM; 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, ...@@ -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); return sctp_sf_do_unexpected_init(ep, asoc, type, arg, commands);
} }
/* Unexpected COOKIE-ECHO handlerfor peer restart (Table 2, action 'A') /* Unexpected COOKIE-ECHO handlerfor peer restart (Table 2, action 'A')
* *
* Section 5.2.4 * Section 5.2.4
...@@ -1212,9 +1320,6 @@ static sctp_disposition_t sctp_sf_do_dupcook_a(const sctp_endpoint_t *ep, ...@@ -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_init_chunk_t *peer_init;
sctp_ulpevent_t *ev; sctp_ulpevent_t *ev;
sctp_chunk_t *repl; 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 /* new_asoc is a brand-new association, so these are not yet
* side effects--it is safe to run them here. * side effects--it is safe to run them here.
...@@ -1223,56 +1328,13 @@ static sctp_disposition_t sctp_sf_do_dupcook_a(const sctp_endpoint_t *ep, ...@@ -1223,56 +1328,13 @@ static sctp_disposition_t sctp_sf_do_dupcook_a(const sctp_endpoint_t *ep,
sctp_process_init(new_asoc, chunk->chunk_hdr->type, sctp_process_init(new_asoc, chunk->chunk_hdr->type,
sctp_source(chunk), peer_init, GFP_ATOMIC); sctp_source(chunk), peer_init, GFP_ATOMIC);
/* Make sure peer is not adding new addresses. */ /* Make sure no new addresses are being added during the
found = 0; * restart. Though this is a pretty complicated attack
new_addr = NULL; * since you'd have to get inside the cookie.
list_for_each(pos, &new_asoc->peer.transport_addr_list) { */
new_addr = list_entry(pos, sctp_transport_t, transports); if (!sctp_sf_check_restart_addrs(new_asoc, asoc, chunk, commands)) {
found = 1; printk("cookie echo check\n");
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));
return SCTP_DISPOSITION_CONSUME; 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 /* For now, fail any unsent/unacked data. Consider the optional
...@@ -1302,7 +1364,6 @@ static sctp_disposition_t sctp_sf_do_dupcook_a(const sctp_endpoint_t *ep, ...@@ -1302,7 +1364,6 @@ static sctp_disposition_t sctp_sf_do_dupcook_a(const sctp_endpoint_t *ep,
nomem_ev: nomem_ev:
sctp_free_chunk(repl); sctp_free_chunk(repl);
nomem: nomem:
return SCTP_DISPOSITION_NOMEM; return SCTP_DISPOSITION_NOMEM;
} }
...@@ -1564,6 +1625,10 @@ sctp_disposition_t sctp_sf_shutdown_pending_abort(const sctp_endpoint_t *ep, ...@@ -1564,6 +1625,10 @@ sctp_disposition_t sctp_sf_shutdown_pending_abort(const sctp_endpoint_t *ep,
void *arg, void *arg,
sctp_cmd_seq_t *commands) 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. */ /* Stop the T5-shutdown guard timer. */
sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, 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, ...@@ -1583,6 +1648,10 @@ sctp_disposition_t sctp_sf_shutdown_sent_abort(const sctp_endpoint_t *ep,
void *arg, void *arg,
sctp_cmd_seq_t *commands) 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. */ /* Stop the T2-shutdown timer. */
sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP,
...@@ -1754,6 +1823,11 @@ sctp_disposition_t sctp_sf_do_9_1_abort(const sctp_endpoint_t *ep, ...@@ -1754,6 +1823,11 @@ sctp_disposition_t sctp_sf_do_9_1_abort(const sctp_endpoint_t *ep,
void *arg, void *arg,
sctp_cmd_seq_t *commands) 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);
/* ASSOC_FAILED will DELETE_TCB. */ /* ASSOC_FAILED will DELETE_TCB. */
sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_NULL()); sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_NULL());
...@@ -1772,8 +1846,10 @@ sctp_disposition_t sctp_sf_cookie_wait_abort(const sctp_endpoint_t *ep, ...@@ -1772,8 +1846,10 @@ sctp_disposition_t sctp_sf_cookie_wait_abort(const sctp_endpoint_t *ep,
void *arg, void *arg,
sctp_cmd_seq_t *commands) sctp_cmd_seq_t *commands)
{ {
/* Check the verification tag. */ sctp_chunk_t *chunk = arg;
/* BUG: WRITE ME. */
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_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
SCTP_STATE(SCTP_STATE_CLOSED)); SCTP_STATE(SCTP_STATE_CLOSED));
...@@ -2699,13 +2775,6 @@ sctp_disposition_t sctp_sf_ootb(const sctp_endpoint_t *ep, ...@@ -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 * the Verification Tag received in the SHUTDOWN ACK and set the
* T-bit in the Chunk Flags to indicate that no TCB was found. * 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 * Inputs
* (endpoint, asoc, type, arg, commands) * (endpoint, asoc, type, arg, commands)
* *
...@@ -2750,6 +2819,31 @@ sctp_disposition_t sctp_sf_shut_8_4_5(const sctp_endpoint_t *ep, ...@@ -2750,6 +2819,31 @@ sctp_disposition_t sctp_sf_shut_8_4_5(const sctp_endpoint_t *ep,
return SCTP_DISPOSITION_NOMEM; return SCTP_DISPOSITION_NOMEM;
} }
/*
* Handle SHUTDOWN ACK in COOKIE_ECHOED or COOKIE_WAIT state.
*
* Verification Tag: 8.5.1 E) Rules for packet carrying a SHUTDOWN ACK
* If the receiver is in COOKIE-ECHOED or COOKIE-WAIT state the
* procedures in section 8.4 SHOULD be followed, in other words it
* should be treated as an Out Of The Blue packet.
* [This means that we do NOT check the Verification Tag on these
* chunks. --piggy ]
*
*/
sctp_disposition_t sctp_sf_do_8_5_1_E_sa(const sctp_endpoint_t *ep,
const sctp_association_t *asoc,
const sctp_subtype_t type,
void *arg,
sctp_cmd_seq_t *commands)
{
/* Although we do have an association in this case, it corresponds
* to a restarted association. So the packet is treated as an OOTB
* packet and the state function that handles OOTB SHUTDOWN_ACK is
* called with a NULL association.
*/
return sctp_sf_shut_8_4_5(ep, NULL, type, arg, commands);
}
/* /*
* Process an unknown chunk. * Process an unknown chunk.
* *
...@@ -2881,8 +2975,7 @@ sctp_disposition_t sctp_sf_discard_chunk(const sctp_endpoint_t *ep, ...@@ -2881,8 +2975,7 @@ sctp_disposition_t sctp_sf_discard_chunk(const sctp_endpoint_t *ep,
* *
* The return value is the disposition of the chunk. * The return value is the disposition of the chunk.
*/ */
sctp_disposition_t sctp_disposition_t sctp_sf_pdiscard(const sctp_endpoint_t *ep,
sctp_sf_pdiscard(const sctp_endpoint_t *ep,
const sctp_association_t *asoc, const sctp_association_t *asoc,
const sctp_subtype_t type, const sctp_subtype_t type,
void *arg, void *arg,
...@@ -4143,9 +4236,16 @@ sctp_packet_t *sctp_ootb_pkt_new(const sctp_association_t *asoc, ...@@ -4143,9 +4236,16 @@ sctp_packet_t *sctp_ootb_pkt_new(const sctp_association_t *asoc,
*/ */
if (asoc) { if (asoc) {
vtag = asoc->peer.i.init_tag; vtag = asoc->peer.i.init_tag;
} else {
/* 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 { } else {
vtag = ntohl(chunk->sctp_hdr->vtag); vtag = ntohl(chunk->sctp_hdr->vtag);
} }
}
/* Make a transport for the bucket, Eliza... */ /* Make a transport for the bucket, Eliza... */
transport = sctp_transport_new(sctp_source(chunk), GFP_ATOMIC); transport = sctp_transport_new(sctp_source(chunk), GFP_ATOMIC);
......
...@@ -271,9 +271,9 @@ sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type, ...@@ -271,9 +271,9 @@ sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type,
/* SCTP_STATE_CLOSED */ \ /* SCTP_STATE_CLOSED */ \
{.fn = sctp_sf_ootb, .name = "sctp_sf_ootb"}, \ {.fn = sctp_sf_ootb, .name = "sctp_sf_ootb"}, \
/* SCTP_STATE_COOKIE_WAIT */ \ /* 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 */ \ /* 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 */ \ /* SCTP_STATE_ESTABLISHED */ \
{.fn = sctp_sf_violation, .name = "sctp_sf_violation"}, \ {.fn = sctp_sf_violation, .name = "sctp_sf_violation"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \ /* SCTP_STATE_SHUTDOWN_PENDING */ \
......
...@@ -726,9 +726,9 @@ SCTP_STATIC void sctp_close(struct sock *sk, long timeout) ...@@ -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 * flags - flags sent or received with the user message, see Section
* 5 for complete description of the flags. * 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(). */ /* BUG: We do not implement the equivalent of wait_for_tcp_memory(). */
SCTP_STATIC int sctp_msghdr_parse(const struct msghdr *, sctp_cmsgs_t *); 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, ...@@ -738,7 +738,7 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
{ {
sctp_opt_t *sp; sctp_opt_t *sp;
sctp_endpoint_t *ep; sctp_endpoint_t *ep;
sctp_association_t *asoc = NULL; sctp_association_t *new_asoc=NULL, *asoc=NULL;
sctp_transport_t *transport; sctp_transport_t *transport;
sctp_chunk_t *chunk = NULL; sctp_chunk_t *chunk = NULL;
sockaddr_storage_t to; sockaddr_storage_t to;
...@@ -821,7 +821,32 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, ...@@ -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 a msg_name has been specified, assume this is to be used. */
if (msg_name) { if (msg_name) {
/* Look for a matching association on the endpoint. */
asoc = sctp_endpoint_lookup_assoc(ep, &to, &transport); 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 { } else {
/* For a peeled-off socket, ignore any associd specified by /* For a peeled-off socket, ignore any associd specified by
* the user with SNDRCVINFO. * the user with SNDRCVINFO.
...@@ -907,11 +932,12 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, ...@@ -907,11 +932,12 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
} }
scope = sctp_scope(&to); scope = sctp_scope(&to);
asoc = sctp_association_new(ep, sk, scope, GFP_KERNEL); new_asoc = sctp_association_new(ep, sk, scope, GFP_KERNEL);
if (!asoc) { if (!new_asoc) {
err = -ENOMEM; err = -ENOMEM;
goto out_unlock; goto out_unlock;
} }
asoc = new_asoc;
/* If the SCTP_INIT ancillary data is specified, set all /* If the SCTP_INIT ancillary data is specified, set all
* the association init values accordingly. * the association init values accordingly.
...@@ -946,7 +972,7 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, ...@@ -946,7 +972,7 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
} }
/* ASSERT: we have a valid association at this point. */ /* 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 /* API 7.1.7, the sndbuf size per association bounds the
* maximum size of data that can be sent in a single send call. * 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, ...@@ -1054,10 +1080,16 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
err = msg_len; err = msg_len;
goto out_unlock; goto out_unlock;
} }
/* If we are already past ASSOCIATE, the lower
* layers are responsible for its cleanup.
*/
goto out_free_chunk;
out_free: out_free:
if (SCTP_STATE_CLOSED == asoc->state) if (new_asoc)
sctp_association_free(asoc); sctp_association_free(asoc);
out_free_chunk:
if (chunk) if (chunk)
sctp_free_chunk(chunk); sctp_free_chunk(chunk);
...@@ -1577,6 +1609,8 @@ SCTP_STATIC int sctp_do_peeloff(sctp_association_t *assoc, struct socket **newso ...@@ -1577,6 +1609,8 @@ SCTP_STATIC int sctp_do_peeloff(sctp_association_t *assoc, struct socket **newso
sctp_endpoint_t *newep; sctp_endpoint_t *newep;
sctp_opt_t *oldsp = sctp_sk(oldsk); sctp_opt_t *oldsp = sctp_sk(oldsk);
sctp_opt_t *newsp; sctp_opt_t *newsp;
struct sk_buff *skb, *tmp;
sctp_ulpevent_t *event;
int err = 0; int err = 0;
/* An association cannot be branched off from an already peeled-off /* 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 ...@@ -1606,6 +1640,17 @@ SCTP_STATIC int sctp_do_peeloff(sctp_association_t *assoc, struct socket **newso
*/ */
newsp->ep = newep; 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 /* Set the type of socket to indicate that it is peeled off from the
* original socket. * original socket.
*/ */
...@@ -1623,39 +1668,46 @@ static inline int sctp_getsockopt_peeloff(struct sock *sk, int len, char *optval ...@@ -1623,39 +1668,46 @@ static inline int sctp_getsockopt_peeloff(struct sock *sk, int len, char *optval
{ {
sctp_peeloff_arg_t peeloff; sctp_peeloff_arg_t peeloff;
struct socket *newsock; struct socket *newsock;
int err, sd; int retval = 0;
sctp_association_t *assoc; sctp_association_t *assoc;
if (len != sizeof(sctp_peeloff_arg_t)) if (len != sizeof(sctp_peeloff_arg_t))
return -EINVAL; return -EINVAL;
if (copy_from_user(&peeloff, optval, len)) if (copy_from_user(&peeloff, optval, len))
return -EFAULT; return -EFAULT;
sctp_lock_sock(sk);
assoc = sctp_id2assoc(sk, peeloff.associd); assoc = sctp_id2assoc(sk, peeloff.associd);
if (NULL == assoc) if (NULL == assoc) {
return -EINVAL; retval = -EINVAL;
goto out_unlock;
}
SCTP_DEBUG_PRINTK("%s: sk: %p assoc: %p\n", __FUNCTION__, sk, assoc); SCTP_DEBUG_PRINTK("%s: sk: %p assoc: %p\n", __FUNCTION__, sk, assoc);
err = sctp_do_peeloff(assoc, &newsock); retval = sctp_do_peeloff(assoc, &newsock);
if (err < 0) if (retval < 0)
return err; goto out_unlock;
/* Map the socket to an unused fd that can be returned to the user. */ /* Map the socket to an unused fd that can be returned to the user. */
sd = sock_map_fd(newsock); retval = sock_map_fd(newsock);
if (sd < 0) { if (retval < 0) {
sock_release(newsock); sock_release(newsock);
return sd; goto out_unlock;
} }
SCTP_DEBUG_PRINTK("%s: sk: %p assoc: %p newsk: %p sd: %d\n", 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. */ /* Return the fd mapped to the new socket. */
peeloff.sd = sd; peeloff.sd = retval;
if (copy_to_user(optval, &peeloff, len)) 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, SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment