Commit a276e83e authored by Jon Grimm's avatar Jon Grimm

[SCTP] Renege to make room for CTSN+1 chunk.

If our receive buffer is full, but this is the most important TSN 
to receive, make room by reneging less important TSNs.  Only renege
if there is a gap and this is the next TSN to fit in the gap.  
parent 8ae0801a
...@@ -68,7 +68,6 @@ typedef enum { ...@@ -68,7 +68,6 @@ typedef enum {
SCTP_CMD_INIT_RESTART, /* High level, do init timer work. */ SCTP_CMD_INIT_RESTART, /* High level, do init timer work. */
SCTP_CMD_INIT_FAILED, /* High level, do init failure work. */ SCTP_CMD_INIT_FAILED, /* High level, do init failure work. */
SCTP_CMD_REPORT_DUP, /* Report a duplicate TSN. */ SCTP_CMD_REPORT_DUP, /* Report a duplicate TSN. */
SCTP_CMD_REPORT_BIGGAP, /* Narc on a TSN (it was too high). */
SCTP_CMD_STRIKE, /* Mark a strike against a transport. */ SCTP_CMD_STRIKE, /* Mark a strike against a transport. */
SCTP_CMD_TRANSMIT, /* Transmit the outqueue. */ SCTP_CMD_TRANSMIT, /* Transmit the outqueue. */
SCTP_CMD_HB_TIMERS_START, /* Start the heartbeat timers. */ SCTP_CMD_HB_TIMERS_START, /* Start the heartbeat timers. */
...@@ -86,8 +85,8 @@ typedef enum { ...@@ -86,8 +85,8 @@ typedef enum {
SCTP_CMD_PURGE_OUTQUEUE, /* Purge all data waiting to be sent. */ SCTP_CMD_PURGE_OUTQUEUE, /* Purge all data waiting to be sent. */
SCTP_CMD_SETUP_T2, /* Hi-level, setup T2-shutdown parms. */ SCTP_CMD_SETUP_T2, /* Hi-level, setup T2-shutdown parms. */
SCTP_CMD_RTO_PENDING, /* Set transport's rto_pending. */ SCTP_CMD_RTO_PENDING, /* Set transport's rto_pending. */
SCTP_CMD_CHUNK_PD, /* Partial data delivery considerations. */ SCTP_CMD_PART_DELIVER, /* Partial data delivery considerations. */
SCTP_CMD_RENEGE, /* Renege data on an association. */
SCTP_CMD_LAST SCTP_CMD_LAST
} sctp_verb_t; } sctp_verb_t;
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
* the Free Software Foundation; either version 2, or (at your option) * the Free Software Foundation; either version 2, or (at your option)
* any later version. * 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 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
* ************************ * ************************
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
...@@ -23,9 +23,14 @@ ...@@ -23,9 +23,14 @@
* the Free Software Foundation, 59 Temple Place - Suite 330, * 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 one of the * Please send any bug reports or fixes you make to the
* following email addresses: * 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:
* Jon Grimm <jgrimm@us.ibm.com> * Jon Grimm <jgrimm@us.ibm.com>
* La Monte H.P. Yarroll <piggy@acm.org> * La Monte H.P. Yarroll <piggy@acm.org>
* Karl Knutson <karl@athena.chicago.il.us> * Karl Knutson <karl@athena.chicago.il.us>
...@@ -153,15 +158,18 @@ static inline __u32 *sctp_tsnmap_get_dups(struct sctp_tsnmap *map) ...@@ -153,15 +158,18 @@ static inline __u32 *sctp_tsnmap_get_dups(struct sctp_tsnmap *map)
return map->dup_tsns; return map->dup_tsns;
} }
/* Mark a duplicate TSN. Note: we limit how many we are willing to /* Mark a duplicate TSN. Note: limit the storage of duplicate TSN
* store and consequently report. * information.
*/ */
static inline void sctp_tsnmap_mark_dup(struct sctp_tsnmap *map, __u32 tsn) static inline void sctp_tsnmap_mark_dup(struct sctp_tsnmap *map, __u32 tsn)
{ {
if (map->num_dup_tsns < SCTP_MAX_DUP_TSNS) if (map->num_dup_tsns < SCTP_MAX_DUP_TSNS)
map->dup_tsns[map->num_dup_tsns++] = tsn; map->dup_tsns[map->num_dup_tsns++] = htonl(tsn);
} }
/* Renege a TSN that was seen. */
void sctp_tsnmap_renege(struct sctp_tsnmap *, __u32 tsn);
/* Is there a gap in the TSN map? */ /* Is there a gap in the TSN map? */
int sctp_tsnmap_has_gap(const struct sctp_tsnmap *); int sctp_tsnmap_has_gap(const struct sctp_tsnmap *);
...@@ -176,6 +184,3 @@ int sctp_tsnmap_next_gap_ack(const struct sctp_tsnmap *, ...@@ -176,6 +184,3 @@ int sctp_tsnmap_next_gap_ack(const struct sctp_tsnmap *,
struct sctp_tsnmap_iter *,__u16 *start, __u16 *end); struct sctp_tsnmap_iter *,__u16 *start, __u16 *end);
#endif /* __sctp_tsnmap_h__ */ #endif /* __sctp_tsnmap_h__ */
...@@ -66,6 +66,9 @@ int sctp_ulpq_tail_data(struct sctp_ulpq *, struct sctp_chunk *, int); ...@@ -66,6 +66,9 @@ int sctp_ulpq_tail_data(struct sctp_ulpq *, struct sctp_chunk *, int);
/* Add a new event for propogation to the ULP. */ /* Add a new event for propogation to the ULP. */
int sctp_ulpq_tail_event(struct sctp_ulpq *, struct sctp_ulpevent *ev); int sctp_ulpq_tail_event(struct sctp_ulpq *, struct sctp_ulpevent *ev);
/* Renege previously received chunks. */
void sctp_ulpq_renege(struct sctp_ulpq *, struct sctp_chunk *, int);
/* Perform partial delivery. */ /* Perform partial delivery. */
void sctp_ulpq_partial_delivery(struct sctp_ulpq *, struct sctp_chunk *, int); void sctp_ulpq_partial_delivery(struct sctp_ulpq *, struct sctp_chunk *, int);
......
...@@ -1004,8 +1004,8 @@ void sctp_assoc_rwnd_increase(sctp_association_t *asoc, int len) ...@@ -1004,8 +1004,8 @@ void sctp_assoc_rwnd_increase(sctp_association_t *asoc, int len)
((asoc->rwnd - asoc->a_rwnd) >= ((asoc->rwnd - asoc->a_rwnd) >=
min_t(__u32, (asoc->base.sk->rcvbuf >> 1), asoc->pmtu))) { min_t(__u32, (asoc->base.sk->rcvbuf >> 1), asoc->pmtu))) {
SCTP_DEBUG_PRINTK("%s: Sending window update SACK- asoc: %p " SCTP_DEBUG_PRINTK("%s: Sending window update SACK- asoc: %p "
"rwnd: %u a_rwnd: %u\n", "rwnd: %u a_rwnd: %u\n", __FUNCTION__,
__FUNCTION__, asoc, asoc->rwnd, asoc->a_rwnd); asoc, asoc->rwnd, asoc->a_rwnd);
sack = sctp_make_sack(asoc); sack = sctp_make_sack(asoc);
if (!sack) if (!sack)
return; return;
......
...@@ -253,7 +253,7 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype, ...@@ -253,7 +253,7 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
{ {
int error = 0; int error = 0;
int force; int force;
sctp_cmd_t *command; sctp_cmd_t *cmd;
sctp_chunk_t *new_obj; sctp_chunk_t *new_obj;
sctp_chunk_t *chunk = NULL; sctp_chunk_t *chunk = NULL;
sctp_packet_t *packet; sctp_packet_t *packet;
...@@ -273,22 +273,22 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype, ...@@ -273,22 +273,22 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
* cmd->handle(x, y, z) * cmd->handle(x, y, z)
* --jgrimm * --jgrimm
*/ */
while (NULL != (command = sctp_next_cmd(commands))) { while (NULL != (cmd = sctp_next_cmd(commands))) {
switch (command->verb) { switch (cmd->verb) {
case SCTP_CMD_NOP: case SCTP_CMD_NOP:
/* Do nothing. */ /* Do nothing. */
break; break;
case SCTP_CMD_NEW_ASOC: case SCTP_CMD_NEW_ASOC:
/* Register a new association. */ /* Register a new association. */
asoc = command->obj.ptr; asoc = cmd->obj.ptr;
/* Register with the endpoint. */ /* Register with the endpoint. */
sctp_endpoint_add_asoc(ep, asoc); sctp_endpoint_add_asoc(ep, asoc);
sctp_hash_established(asoc); sctp_hash_established(asoc);
break; break;
case SCTP_CMD_UPDATE_ASSOC: case SCTP_CMD_UPDATE_ASSOC:
sctp_assoc_update(asoc, command->obj.ptr); sctp_assoc_update(asoc, cmd->obj.ptr);
break; break;
case SCTP_CMD_PURGE_OUTQUEUE: case SCTP_CMD_PURGE_OUTQUEUE:
...@@ -304,13 +304,12 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype, ...@@ -304,13 +304,12 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
case SCTP_CMD_NEW_STATE: case SCTP_CMD_NEW_STATE:
/* Enter a new state. */ /* Enter a new state. */
sctp_cmd_new_state(commands, asoc, command->obj.state); sctp_cmd_new_state(commands, asoc, cmd->obj.state);
break; break;
case SCTP_CMD_REPORT_TSN: case SCTP_CMD_REPORT_TSN:
/* Record the arrival of a TSN. */ /* Record the arrival of a TSN. */
sctp_tsnmap_mark(&asoc->peer.tsn_map, sctp_tsnmap_mark(&asoc->peer.tsn_map, cmd->obj.u32);
command->obj.u32);
break; break;
case SCTP_CMD_GEN_SACK: case SCTP_CMD_GEN_SACK:
...@@ -319,14 +318,14 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype, ...@@ -319,14 +318,14 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
* the packet and MAYBE generate a SACK, or * the packet and MAYBE generate a SACK, or
* force a SACK out. * force a SACK out.
*/ */
force = command->obj.i32; force = cmd->obj.i32;
error = sctp_gen_sack(asoc, force, commands); error = sctp_gen_sack(asoc, force, commands);
break; break;
case SCTP_CMD_PROCESS_SACK: case SCTP_CMD_PROCESS_SACK:
/* Process an inbound SACK. */ /* Process an inbound SACK. */
error = sctp_cmd_process_sack(commands, asoc, error = sctp_cmd_process_sack(commands, asoc,
command->obj.ptr); cmd->obj.ptr);
break; break;
case SCTP_CMD_GEN_INIT_ACK: case SCTP_CMD_GEN_INIT_ACK:
...@@ -347,16 +346,15 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype, ...@@ -347,16 +346,15 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
* layer which will bail. * layer which will bail.
*/ */
error = sctp_cmd_process_init(commands, asoc, chunk, error = sctp_cmd_process_init(commands, asoc, chunk,
command->obj.ptr, cmd->obj.ptr, priority);
priority);
break; break;
case SCTP_CMD_GEN_COOKIE_ECHO: case SCTP_CMD_GEN_COOKIE_ECHO:
/* Generate a COOKIE ECHO chunk. */ /* Generate a COOKIE ECHO chunk. */
new_obj = sctp_make_cookie_echo(asoc, chunk); new_obj = sctp_make_cookie_echo(asoc, chunk);
if (!new_obj) { if (!new_obj) {
if (command->obj.ptr) if (cmd->obj.ptr)
sctp_free_chunk(command->obj.ptr); sctp_free_chunk(cmd->obj.ptr);
goto nomem; goto nomem;
} }
sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, sctp_add_cmd_sf(commands, SCTP_CMD_REPLY,
...@@ -365,9 +363,9 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype, ...@@ -365,9 +363,9 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
/* If there is an ERROR chunk to be sent along with /* If there is an ERROR chunk to be sent along with
* the COOKIE_ECHO, send it, too. * the COOKIE_ECHO, send it, too.
*/ */
if (command->obj.ptr) if (cmd->obj.ptr)
sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, sctp_add_cmd_sf(commands, SCTP_CMD_REPLY,
SCTP_CHUNK(command->obj.ptr)); SCTP_CHUNK(cmd->obj.ptr));
break; break;
case SCTP_CMD_GEN_SHUTDOWN: case SCTP_CMD_GEN_SHUTDOWN:
...@@ -387,43 +385,36 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype, ...@@ -387,43 +385,36 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
case SCTP_CMD_CHUNK_ULP: case SCTP_CMD_CHUNK_ULP:
/* Send a chunk to the sockets layer. */ /* Send a chunk to the sockets layer. */
SCTP_DEBUG_PRINTK("sm_sideff: %s %p, %s %p.\n", SCTP_DEBUG_PRINTK("sm_sideff: %s %p, %s %p.\n",
"chunk_up:", "chunk_up:", cmd->obj.ptr,
command->obj.ptr, "ulpq:", &asoc->ulpq);
"ulpq:", sctp_ulpq_tail_data(&asoc->ulpq, cmd->obj.ptr,
&asoc->ulpq);
sctp_ulpq_tail_data(&asoc->ulpq,
command->obj.ptr,
GFP_ATOMIC); GFP_ATOMIC);
break; break;
case SCTP_CMD_EVENT_ULP: case SCTP_CMD_EVENT_ULP:
/* Send a notification to the sockets layer. */ /* Send a notification to the sockets layer. */
SCTP_DEBUG_PRINTK("sm_sideff: %s %p, %s %p.\n", SCTP_DEBUG_PRINTK("sm_sideff: %s %p, %s %p.\n",
"event_up:", "event_up:",cmd->obj.ptr,
command->obj.ptr, "ulpq:",&asoc->ulpq);
"ulpq:", sctp_ulpq_tail_event(&asoc->ulpq, cmd->obj.ptr);
&asoc->ulpq);
sctp_ulpq_tail_event(&asoc->ulpq,
command->obj.ptr);
break; break;
case SCTP_CMD_REPLY: case SCTP_CMD_REPLY:
/* Send a chunk to our peer. */ /* Send a chunk to our peer. */
error = sctp_outq_tail(&asoc->outqueue, error = sctp_outq_tail(&asoc->outqueue,
command->obj.ptr); cmd->obj.ptr);
break; break;
case SCTP_CMD_SEND_PKT: case SCTP_CMD_SEND_PKT:
/* Send a full packet to our peer. */ /* Send a full packet to our peer. */
packet = command->obj.ptr; packet = cmd->obj.ptr;
sctp_packet_transmit(packet); sctp_packet_transmit(packet);
sctp_ootb_pkt_free(packet); sctp_ootb_pkt_free(packet);
break; break;
case SCTP_CMD_RETRAN: case SCTP_CMD_RETRAN:
/* Mark a transport for retransmission. */ /* Mark a transport for retransmission. */
sctp_retransmit(&asoc->outqueue, sctp_retransmit(&asoc->outqueue, cmd->obj.transport,
command->obj.transport,
SCTP_RETRANSMIT_T3_RTX); SCTP_RETRANSMIT_T3_RTX);
break; break;
...@@ -434,32 +425,30 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype, ...@@ -434,32 +425,30 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
case SCTP_CMD_ECN_CE: case SCTP_CMD_ECN_CE:
/* Do delayed CE processing. */ /* Do delayed CE processing. */
sctp_do_ecn_ce_work(asoc, command->obj.u32); sctp_do_ecn_ce_work(asoc, cmd->obj.u32);
break; break;
case SCTP_CMD_ECN_ECNE: case SCTP_CMD_ECN_ECNE:
/* Do delayed ECNE processing. */ /* Do delayed ECNE processing. */
new_obj = sctp_do_ecn_ecne_work(asoc, new_obj = sctp_do_ecn_ecne_work(asoc, cmd->obj.u32,
command->obj.u32,
chunk); chunk);
if (new_obj) { if (new_obj)
sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, sctp_add_cmd_sf(commands, SCTP_CMD_REPLY,
SCTP_CHUNK(new_obj)); SCTP_CHUNK(new_obj));
}
break; break;
case SCTP_CMD_ECN_CWR: case SCTP_CMD_ECN_CWR:
/* Do delayed CWR processing. */ /* Do delayed CWR processing. */
sctp_do_ecn_cwr_work(asoc, command->obj.u32); sctp_do_ecn_cwr_work(asoc, cmd->obj.u32);
break; break;
case SCTP_CMD_SETUP_T2: case SCTP_CMD_SETUP_T2:
sctp_cmd_setup_t2(commands, asoc, command->obj.ptr); sctp_cmd_setup_t2(commands, asoc, cmd->obj.ptr);
break; break;
case SCTP_CMD_TIMER_START: case SCTP_CMD_TIMER_START:
timer = &asoc->timers[command->obj.to]; timer = &asoc->timers[cmd->obj.to];
timeout = asoc->timeouts[command->obj.to]; timeout = asoc->timeouts[cmd->obj.to];
if (!timeout) if (!timeout)
BUG(); BUG();
...@@ -469,29 +458,28 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype, ...@@ -469,29 +458,28 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
break; break;
case SCTP_CMD_TIMER_RESTART: case SCTP_CMD_TIMER_RESTART:
timer = &asoc->timers[command->obj.to]; timer = &asoc->timers[cmd->obj.to];
timeout = asoc->timeouts[command->obj.to]; timeout = asoc->timeouts[cmd->obj.to];
if (!mod_timer(timer, jiffies + timeout)) if (!mod_timer(timer, jiffies + timeout))
sctp_association_hold(asoc); sctp_association_hold(asoc);
break; break;
case SCTP_CMD_TIMER_STOP: case SCTP_CMD_TIMER_STOP:
timer = &asoc->timers[command->obj.to]; timer = &asoc->timers[cmd->obj.to];
if (timer_pending(timer) && del_timer(timer)) if (timer_pending(timer) && del_timer(timer))
sctp_association_put(asoc); sctp_association_put(asoc);
break; break;
case SCTP_CMD_INIT_RESTART: case SCTP_CMD_INIT_RESTART:
/* Do the needed accounting and updates /* Do the needed accounting and updates
* associated with restarting an initialization * associated with restarting an initialization
* timer. * timer.
*/ */
asoc->counters[SCTP_COUNTER_INIT_ERROR]++; asoc->counters[SCTP_COUNTER_INIT_ERROR]++;
asoc->timeouts[command->obj.to] *= 2; asoc->timeouts[cmd->obj.to] *= 2;
if (asoc->timeouts[command->obj.to] > if (asoc->timeouts[cmd->obj.to] >
asoc->max_init_timeo) { asoc->max_init_timeo) {
asoc->timeouts[command->obj.to] = asoc->timeouts[cmd->obj.to] =
asoc->max_init_timeo; asoc->max_init_timeo;
} }
...@@ -506,7 +494,7 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype, ...@@ -506,7 +494,7 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
sctp_add_cmd_sf(commands, sctp_add_cmd_sf(commands,
SCTP_CMD_TIMER_RESTART, SCTP_CMD_TIMER_RESTART,
SCTP_TO(command->obj.to)); SCTP_TO(cmd->obj.to));
break; break;
case SCTP_CMD_INIT_FAILED: case SCTP_CMD_INIT_FAILED:
...@@ -519,23 +507,16 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype, ...@@ -519,23 +507,16 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
break; break;
case SCTP_CMD_COUNTER_INC: case SCTP_CMD_COUNTER_INC:
asoc->counters[command->obj.counter]++; asoc->counters[cmd->obj.counter]++;
break; break;
case SCTP_CMD_COUNTER_RESET: case SCTP_CMD_COUNTER_RESET:
asoc->counters[command->obj.counter] = 0; asoc->counters[cmd->obj.counter] = 0;
break; break;
case SCTP_CMD_REPORT_DUP: case SCTP_CMD_REPORT_DUP:
sctp_tsnmap_mark_dup(&asoc->peer.tsn_map, sctp_tsnmap_mark_dup(&asoc->peer.tsn_map,
ntohl(command->obj.u32)); cmd->obj.u32);
break;
case SCTP_CMD_REPORT_BIGGAP:
SCTP_DEBUG_PRINTK("Big gap: %x to %x\n",
sctp_tsnmap_get_ctsn(
&asoc->peer.tsn_map),
command->obj.u32);
break; break;
case SCTP_CMD_REPORT_BAD_TAG: case SCTP_CMD_REPORT_BAD_TAG:
...@@ -544,17 +525,16 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype, ...@@ -544,17 +525,16 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
case SCTP_CMD_STRIKE: case SCTP_CMD_STRIKE:
/* Mark one strike against a transport. */ /* Mark one strike against a transport. */
sctp_do_8_2_transport_strike(asoc, sctp_do_8_2_transport_strike(asoc, cmd->obj.transport);
command->obj.transport);
break; break;
case SCTP_CMD_TRANSPORT_RESET: case SCTP_CMD_TRANSPORT_RESET:
t = command->obj.transport; t = cmd->obj.transport;
sctp_cmd_transport_reset(commands, asoc, t); sctp_cmd_transport_reset(commands, asoc, t);
break; break;
case SCTP_CMD_TRANSPORT_ON: case SCTP_CMD_TRANSPORT_ON:
t = command->obj.transport; t = cmd->obj.transport;
sctp_cmd_transport_on(commands, asoc, t, chunk); sctp_cmd_transport_on(commands, asoc, t, chunk);
break; break;
...@@ -563,7 +543,7 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype, ...@@ -563,7 +543,7 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
break; break;
case SCTP_CMD_HB_TIMER_UPDATE: case SCTP_CMD_HB_TIMER_UPDATE:
t = command->obj.transport; t = cmd->obj.transport;
sctp_cmd_hb_timer_update(commands, asoc, t); sctp_cmd_hb_timer_update(commands, asoc, t);
break; break;
...@@ -572,17 +552,16 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype, ...@@ -572,17 +552,16 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
break; break;
case SCTP_CMD_REPORT_ERROR: case SCTP_CMD_REPORT_ERROR:
error = command->obj.error; error = cmd->obj.error;
break; break;
case SCTP_CMD_PROCESS_CTSN: case SCTP_CMD_PROCESS_CTSN:
/* Dummy up a SACK for processing. */ /* Dummy up a SACK for processing. */
sackh.cum_tsn_ack = command->obj.u32; sackh.cum_tsn_ack = cmd->obj.u32;
sackh.a_rwnd = 0; sackh.a_rwnd = 0;
sackh.num_gap_ack_blocks = 0; sackh.num_gap_ack_blocks = 0;
sackh.num_dup_tsns = 0; sackh.num_dup_tsns = 0;
sctp_add_cmd_sf(commands, sctp_add_cmd_sf(commands, SCTP_CMD_PROCESS_SACK,
SCTP_CMD_PROCESS_SACK,
SCTP_SACKH(&sackh)); SCTP_SACKH(&sackh));
break; break;
...@@ -592,20 +571,23 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype, ...@@ -592,20 +571,23 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
break; break;
case SCTP_CMD_RTO_PENDING: case SCTP_CMD_RTO_PENDING:
t = command->obj.transport; t = cmd->obj.transport;
t->rto_pending = 1; t->rto_pending = 1;
break; break;
case SCTP_CMD_CHUNK_PD: case SCTP_CMD_PART_DELIVER:
/* Send a chunk to the sockets layer. */ sctp_ulpq_partial_delivery(&asoc->ulpq, cmd->obj.ptr,
sctp_ulpq_partial_delivery(&asoc->ulpq, GFP_ATOMIC);
command->obj.ptr, break;
case SCTP_CMD_RENEGE:
sctp_ulpq_renege(&asoc->ulpq, cmd->obj.ptr,
GFP_ATOMIC); GFP_ATOMIC);
break; break;
default: default:
printk(KERN_WARNING "Impossible command: %u, %p\n", printk(KERN_WARNING "Impossible command: %u, %p\n",
command->verb, command->obj.ptr); cmd->verb, cmd->obj.ptr);
break; break;
}; };
if (error) if (error)
......
...@@ -682,7 +682,6 @@ sctp_disposition_t sctp_sf_do_5_1E_ca(const sctp_endpoint_t *ep, ...@@ -682,7 +682,6 @@ sctp_disposition_t sctp_sf_do_5_1E_ca(const sctp_endpoint_t *ep,
sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP, SCTP_ULPEVENT(ev)); sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP, SCTP_ULPEVENT(ev));
return SCTP_DISPOSITION_CONSUME; return SCTP_DISPOSITION_CONSUME;
nomem: nomem:
return SCTP_DISPOSITION_NOMEM; return SCTP_DISPOSITION_NOMEM;
} }
...@@ -2274,7 +2273,6 @@ sctp_disposition_t sctp_sf_eat_data_6_2(const sctp_endpoint_t *ep, ...@@ -2274,7 +2273,6 @@ sctp_disposition_t sctp_sf_eat_data_6_2(const sctp_endpoint_t *ep,
* that the value in the Verification Tag field of the * that the value in the Verification Tag field of the
* received SCTP packet matches its own Tag. * received SCTP packet matches its own Tag.
*/ */
if (ntohl(chunk->sctp_hdr->vtag) != asoc->c.my_vtag) { if (ntohl(chunk->sctp_hdr->vtag) != asoc->c.my_vtag) {
sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_BAD_TAG, sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_BAD_TAG,
SCTP_NULL()); SCTP_NULL());
...@@ -2339,18 +2337,26 @@ sctp_disposition_t sctp_sf_eat_data_6_2(const sctp_endpoint_t *ep, ...@@ -2339,18 +2337,26 @@ sctp_disposition_t sctp_sf_eat_data_6_2(const sctp_endpoint_t *ep,
/* Even if we don't accept this chunk there is /* Even if we don't accept this chunk there is
* memory pressure. * memory pressure.
*/ */
sctp_add_cmd_sf(commands, SCTP_CMD_CHUNK_PD, SCTP_NULL()); sctp_add_cmd_sf(commands, SCTP_CMD_PART_DELIVER, SCTP_NULL());
} }
/* Spill over rwnd a little bit. Note: While allowed, this spill over
* seems a bit troublesome in that frag_point varies based on
* PMTU. In cases, such as loopback, this might be a rather
* large spill over.
*/
if (asoc->rwnd_over || (datalen > asoc->rwnd + asoc->frag_point)) { if (asoc->rwnd_over || (datalen > asoc->rwnd + asoc->frag_point)) {
/* If this is the next TSN, consider reneging to make
/* There is absolutely no room, but this is the most * room. Note: Playing nice with a confused sender. A
* important tsn that we are waiting on, try to * malicious sender can still eat up all our buffer
* to partial deliver or renege to make room. * space and in the future we may want to detect and
* do more drastic reneging.
*/ */
if ((sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map) + 1) == tsn) { if (sctp_tsnmap_has_gap(&asoc->peer.tsn_map) &&
deliver = SCTP_CMD_CHUNK_PD; (sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map) + 1) == tsn) {
SCTP_DEBUG_PRINTK("Reneging for tsn:%u\n", tsn);
deliver = SCTP_CMD_RENEGE;
} else { } else {
SCTP_DEBUG_PRINTK("Discard tsn: %u len: %Zd, " SCTP_DEBUG_PRINTK("Discard tsn: %u len: %Zd, "
"rwnd: %d\n", tsn, datalen, "rwnd: %d\n", tsn, datalen,
...@@ -2379,21 +2385,23 @@ sctp_disposition_t sctp_sf_eat_data_6_2(const sctp_endpoint_t *ep, ...@@ -2379,21 +2385,23 @@ sctp_disposition_t sctp_sf_eat_data_6_2(const sctp_endpoint_t *ep,
sctp_add_cmd_sf(commands, SCTP_CMD_DISCARD_PACKET,SCTP_NULL()); sctp_add_cmd_sf(commands, SCTP_CMD_DISCARD_PACKET,SCTP_NULL());
sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_NULL()); sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_NULL());
SCTP_INC_STATS(SctpAborteds); SCTP_INC_STATS(SctpAborteds);
SCTP_INC_STATS(SctpCurrEstab); SCTP_DEC_STATS(SctpCurrEstab);
return SCTP_DISPOSITION_CONSUME; return SCTP_DISPOSITION_CONSUME;
} }
/* If definately accepting the DATA chunk, record its TSN, otherwise /* If definately accepting the DATA chunk, record its TSN, otherwise
* wait for renege processing. * wait for renege processing.
*/ */
if (deliver != SCTP_CMD_CHUNK_PD) { if (SCTP_CMD_CHUNK_ULP == deliver)
sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_TSN, SCTP_U32(tsn)); sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_TSN, SCTP_U32(tsn));
/* Note: Some chunks may get overcounted (if we drop) or overcounted
* if we renege and the chunk arrives again.
*/
if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED) if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED)
SCTP_INC_STATS(SctpInUnorderChunks); SCTP_INC_STATS(SctpInUnorderChunks);
else else
SCTP_INC_STATS(SctpInOrderChunks); SCTP_INC_STATS(SctpInOrderChunks);
}
/* RFC 2960 6.5 Stream Identifier and Stream Sequence Number /* RFC 2960 6.5 Stream Identifier and Stream Sequence Number
* *
...@@ -2592,7 +2600,7 @@ sctp_disposition_t sctp_sf_eat_data_fast_4_4(const sctp_endpoint_t *ep, ...@@ -2592,7 +2600,7 @@ sctp_disposition_t sctp_sf_eat_data_fast_4_4(const sctp_endpoint_t *ep,
sctp_add_cmd_sf(commands, SCTP_CMD_DISCARD_PACKET,SCTP_NULL()); sctp_add_cmd_sf(commands, SCTP_CMD_DISCARD_PACKET,SCTP_NULL());
sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_NULL()); sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_NULL());
SCTP_INC_STATS(SctpAborteds); SCTP_INC_STATS(SctpAborteds);
SCTP_INC_STATS(SctpCurrEstab); SCTP_DEC_STATS(SctpCurrEstab);
return SCTP_DISPOSITION_CONSUME; return SCTP_DISPOSITION_CONSUME;
} }
......
...@@ -385,3 +385,23 @@ static void sctp_tsnmap_find_gap_ack(__u8 *map, __u16 off, ...@@ -385,3 +385,23 @@ static void sctp_tsnmap_find_gap_ack(__u8 *map, __u16 off,
} }
} }
} }
/* Renege that we have seen a TSN. */
void sctp_tsnmap_renege(struct sctp_tsnmap *map, __u32 tsn)
{
__s32 gap;
if (TSN_lt(tsn, map->base_tsn))
return;
if (!TSN_lt(tsn, map->base_tsn + map->len + map->len))
return;
/* Assert: TSN is in range. */
gap = tsn - map->base_tsn;
/* Pretend we never saw the TSN. */
if (gap < map->len)
map->tsn_map[gap] = 0;
else
map->overflow_map[gap - map->len] = 0;
}
...@@ -655,32 +655,119 @@ static inline struct sctp_ulpevent *sctp_ulpq_order(struct sctp_ulpq *ulpq, ...@@ -655,32 +655,119 @@ static inline struct sctp_ulpevent *sctp_ulpq_order(struct sctp_ulpq *ulpq,
return event; return event;
} }
/* Renege 'needed' bytes from the ordering queue. */
static __u16 sctp_ulpq_renege_order(struct sctp_ulpq *ulpq, __u16 needed)
{
__u16 freed = 0;
__u32 tsn;
struct sk_buff *skb;
struct sctp_ulpevent *event;
struct sctp_tsnmap *tsnmap;
tsnmap = &ulpq->asoc->peer.tsn_map;
while ((skb = __skb_dequeue_tail(&ulpq->lobby))) {
freed += skb_headlen(skb);
event = sctp_skb2event(skb);
tsn = event->sndrcvinfo.sinfo_tsn;
sctp_ulpevent_free(event);
sctp_tsnmap_renege(tsnmap, tsn);
if (freed >= needed)
return freed;
}
return freed;
}
/* Renege 'needed' bytes from the reassembly queue. */
static __u16 sctp_ulpq_renege_frags(struct sctp_ulpq *ulpq, __u16 needed)
{
__u16 freed = 0;
__u32 tsn;
struct sk_buff *skb;
struct sctp_ulpevent *event;
struct sctp_tsnmap *tsnmap;
tsnmap = &ulpq->asoc->peer.tsn_map;
/* Walk backwards through the list, reneges the newest tsns. */
while ((skb = __skb_dequeue_tail(&ulpq->reasm))) {
freed += skb_headlen(skb);
event = sctp_skb2event(skb);
tsn = event->sndrcvinfo.sinfo_tsn;
sctp_ulpevent_free(event);
sctp_tsnmap_renege(tsnmap, tsn);
if (freed >= needed)
return freed;
}
return freed;
}
/* Partial deliver the first message as there is pressure on rwnd. */ /* Partial deliver the first message as there is pressure on rwnd. */
void sctp_ulpq_partial_delivery(struct sctp_ulpq *ulpq, void sctp_ulpq_partial_delivery(struct sctp_ulpq *ulpq,
struct sctp_chunk *chunk, int priority) struct sctp_chunk *chunk, int priority)
{ {
struct sctp_ulpevent *event; struct sctp_ulpevent *event;
struct sctp_association *asoc;
asoc = ulpq->asoc;
/* Are we already in partial delivery mode? */ /* Are we already in partial delivery mode? */
if (!sctp_sk(ulpq->asoc->base.sk)->pd_mode) { if (!sctp_sk(asoc->base.sk)->pd_mode) {
/* Is partial delivery possible? */ /* Is partial delivery possible? */
event = sctp_ulpq_retrieve_first(ulpq); event = sctp_ulpq_retrieve_first(ulpq);
/* Send event to the ULP. */ /* Send event to the ULP. */
if (event) { if (event) {
sctp_ulpq_tail_event(ulpq, event); sctp_ulpq_tail_event(ulpq, event);
sctp_sk(ulpq->asoc->base.sk)->pd_mode = 1; sctp_sk(asoc->base.sk)->pd_mode = 1;
ulpq->pd_mode = 1; ulpq->pd_mode = 1;
return; return;
} }
} }
}
/* Assert: Either already in partial delivery mode or partial /* Renege some packets to make room for an incoming chunk. */
* delivery wasn't possible, so now the only recourse is void sctp_ulpq_renege(struct sctp_ulpq *ulpq, struct sctp_chunk *chunk,
* to renege. FIXME: Add renege support starts here. int priority)
*/ {
struct sctp_association *asoc;
__u16 needed, freed;
asoc = ulpq->asoc;
if (chunk) {
needed = ntohs(chunk->chunk_hdr->length);
needed -= sizeof(sctp_data_chunk_t);
} else
needed = SCTP_DEFAULT_MAXWINDOW;
freed = 0;
if (skb_queue_empty(&asoc->base.sk->receive_queue)) {
freed = sctp_ulpq_renege_order(ulpq, needed);
if (freed < needed) {
freed += sctp_ulpq_renege_frags(ulpq, needed - freed);
}
}
/* If able to free enough room, accept this chunk. */
if (chunk && (freed >= needed)) {
__u32 tsn;
tsn = ntohl(chunk->subh.data_hdr->tsn);
sctp_tsnmap_mark(&asoc->peer.tsn_map, tsn);
sctp_ulpq_tail_data(ulpq, chunk, priority);
sctp_ulpq_partial_delivery(ulpq, chunk, priority);
}
return;
} }
/* Notify the application if an association is aborted and in /* Notify the application if an association is aborted and in
* partial delivery mode. Send up any pending received messages. * partial delivery mode. Send up any pending received messages.
*/ */
......
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