SCTP_XMIT_OK,
SCTP_XMIT_PMTU_FULL,
SCTP_XMIT_RWND_FULL,
- SCTP_XMIT_MUST_FRAG,
SCTP_XMIT_NAGLE_DELAY,
} sctp_xmit_t;
*/
struct sctp_transport *transport;
- /* Allow a callback for getting a high priority chunk
- * bundled early into the packet (This is used for ECNE).
- */
- sctp_packet_phandler_t *get_prepend_chunk;
-
- /* This packet should advertise ECN capability to the network
- * via the ECT bit.
- */
- char ecn_capable;
-
/* This packet contains a COOKIE-ECHO chunk. */
char has_cookie_echo;
int malloced;
};
-typedef int (sctp_outq_thandler_t)(struct sctp_outq *, void *);
-typedef int (sctp_outq_ehandler_t)(struct sctp_outq *);
-typedef struct sctp_packet *(sctp_outq_ohandler_init_t)
- (struct sctp_packet *,
- struct sctp_transport *,
- __u16 sport,
- __u16 dport);
-typedef struct sctp_packet *(sctp_outq_ohandler_config_t)
- (struct sctp_packet *,
- __u32 vtag,
- int ecn_capable,
- sctp_packet_phandler_t *get_prepend_chunk);
-typedef sctp_xmit_t (sctp_outq_ohandler_t)(struct sctp_packet *,
- struct sctp_chunk *);
-typedef int (sctp_outq_ohandler_force_t)(struct sctp_packet *);
-
-sctp_outq_ohandler_init_t sctp_packet_init;
-sctp_outq_ohandler_config_t sctp_packet_config;
-sctp_outq_ohandler_t sctp_packet_append_chunk;
-sctp_outq_ohandler_t sctp_packet_transmit_chunk;
-sctp_outq_ohandler_force_t sctp_packet_transmit;
+struct sctp_packet *sctp_packet_init(struct sctp_packet *,
+ struct sctp_transport *,
+ __u16 sport, __u16 dport);
+struct sctp_packet *sctp_packet_config(struct sctp_packet *, __u32 vtag, int);
+sctp_xmit_t sctp_packet_transmit_chunk(struct sctp_packet *,
+ struct sctp_chunk *);
+sctp_xmit_t sctp_packet_append_chunk(struct sctp_packet *,
+ struct sctp_chunk *);
+int sctp_packet_transmit(struct sctp_packet *);
void sctp_packet_free(struct sctp_packet *);
static inline int sctp_packet_empty(struct sctp_packet *packet)
*/
struct list_head retransmit;
- /* Call these functions to send chunks down to the next lower
- * layer. This is always sctp_packet, but we separate the two
- * structures to make testing simpler.
- */
- sctp_outq_ohandler_init_t *init_output;
- sctp_outq_ohandler_config_t *config_output;
- sctp_outq_ohandler_t *append_output;
- sctp_outq_ohandler_t *build_output;
- sctp_outq_ohandler_force_t *force_output;
-
/* How many unackd bytes do we have in-flight? */
__u32 outstanding_bytes;
int sctp_outq_flush(struct sctp_outq *, int);
int sctp_outq_sack(struct sctp_outq *, struct sctp_sackhdr *);
int sctp_outq_is_empty(const struct sctp_outq *);
-int sctp_outq_set_output_handlers(struct sctp_outq *,
- sctp_outq_ohandler_init_t init,
- sctp_outq_ohandler_config_t config,
- sctp_outq_ohandler_t append,
- sctp_outq_ohandler_t build,
- sctp_outq_ohandler_force_t force);
void sctp_outq_restart(struct sctp_outq *);
void sctp_retransmit(struct sctp_outq *, struct sctp_transport *,
/* Create an output queue. */
sctp_outq_init(asoc, &asoc->outqueue);
- sctp_outq_set_output_handlers(&asoc->outqueue,
- sctp_packet_init,
- sctp_packet_config,
- sctp_packet_append_chunk,
- sctp_packet_transmit_chunk,
- sctp_packet_transmit);
if (!sctp_ulpq_init(&asoc->ulpq, asoc))
goto fail_init;
/* The asoc->peer.port might not be meaningful yet, but
* initialize the packet structure anyway.
*/
- (asoc->outqueue.init_output)(&peer->packet,
- peer,
- asoc->base.bind_addr.port,
- asoc->peer.port);
+ sctp_packet_init(&peer->packet, peer, asoc->base.bind_addr.port,
+ asoc->peer.port);
/* 7.2.1 Slow-Start
*
#include <net/sctp/sm.h>
/* Forward declarations for private helpers. */
-static void sctp_packet_reset(struct sctp_packet *packet);
static sctp_xmit_t sctp_packet_append_data(struct sctp_packet *packet,
struct sctp_chunk *chunk);
/* Config a packet.
- * This appears to be a followup set of initializations.)
+ * This appears to be a followup set of initializations.
*/
struct sctp_packet *sctp_packet_config(struct sctp_packet *packet,
- __u32 vtag, int ecn_capable,
- sctp_packet_phandler_t *prepend_handler)
+ __u32 vtag, int ecn_capable)
{
+ struct sctp_chunk *chunk = NULL;
+
+ SCTP_DEBUG_PRINTK("%s: packet:%p vtag:0x%x\n", __FUNCTION__,
+ packet, vtag);
+
packet->vtag = vtag;
- packet->ecn_capable = ecn_capable;
- packet->get_prepend_chunk = prepend_handler;
packet->has_cookie_echo = 0;
packet->has_sack = 0;
packet->ipfragok = 0;
- /* We might need to call the prepend_handler right away. */
- if (sctp_packet_empty(packet))
- sctp_packet_reset(packet);
+ if (ecn_capable && sctp_packet_empty(packet)) {
+ chunk = sctp_get_ecne_prepend(packet->transport->asoc);
+
+ /* If there a is a prepend chunk stick it on the list before
+ * any other chunks get appended.
+ */
+ if (chunk)
+ sctp_packet_append_chunk(packet, chunk);
+ }
+
return packet;
}
struct sctp_association *asoc = transport->asoc;
size_t overhead;
+ SCTP_DEBUG_PRINTK("%s: packet:%p transport:%p\n", __FUNCTION__,
+ packet, transport);
+
packet->transport = transport;
packet->source_port = sport;
packet->destination_port = dport;
packet->overhead = overhead;
packet->size = overhead;
packet->vtag = 0;
- packet->ecn_capable = 0;
- packet->get_prepend_chunk = NULL;
packet->has_cookie_echo = 0;
packet->has_sack = 0;
packet->ipfragok = 0;
packet->malloced = 0;
- sctp_packet_reset(packet);
return packet;
}
{
struct sctp_chunk *chunk;
+ SCTP_DEBUG_PRINTK("%s: packet:%p\n", __FUNCTION__, packet);
+
while ((chunk = (struct sctp_chunk *)__skb_dequeue(&packet->chunks)))
sctp_chunk_free(chunk);
sctp_xmit_t retval;
int error = 0;
+ SCTP_DEBUG_PRINTK("%s: packet:%p chunk:%p\n", __FUNCTION__,
+ packet, chunk);
+
switch ((retval = (sctp_packet_append_chunk(packet, chunk)))) {
case SCTP_XMIT_PMTU_FULL:
if (!packet->has_cookie_echo) {
}
break;
- case SCTP_XMIT_MUST_FRAG:
case SCTP_XMIT_RWND_FULL:
case SCTP_XMIT_OK:
case SCTP_XMIT_NAGLE_DELAY:
size_t pmtu;
int too_big;
+ SCTP_DEBUG_PRINTK("%s: packet:%p chunk:%p\n", __FUNCTION__, packet,
+ chunk);
+
retval = sctp_packet_bundle_sack(packet, chunk);
psize = packet->size;
retval = SCTP_XMIT_PMTU_FULL;
goto finish;
}
- } else {
- /* The chunk fits in the packet. */
- goto append;
}
append:
__u8 has_data = 0;
struct dst_entry *dst;
+ SCTP_DEBUG_PRINTK("%s: packet:%p\n", __FUNCTION__, packet);
+
/* Do NOT generate a chunkless packet. */
chunk = (struct sctp_chunk *)skb_peek(&packet->chunks);
if (unlikely(!chunk))
* 2nd Level Abstractions
********************************************************************/
-/*
- * This private function resets the packet to a fresh state.
- */
-static void sctp_packet_reset(struct sctp_packet *packet)
-{
- struct sctp_chunk *chunk = NULL;
-
- packet->size = packet->overhead;
-
- if (packet->get_prepend_chunk)
- chunk = packet->get_prepend_chunk(packet->transport->asoc);
-
- /* If there a is a prepend chunk stick it on the list before
- * any other chunks get appended.
- */
- if (chunk)
- sctp_packet_append_chunk(packet, chunk);
-}
-
/* This private function handles the specifics of appending DATA chunks. */
static sctp_xmit_t sctp_packet_append_data(struct sctp_packet *packet,
struct sctp_chunk *chunk)
INIT_LIST_HEAD(&q->retransmit);
INIT_LIST_HEAD(&q->sacked);
- q->init_output = NULL;
- q->config_output = NULL;
- q->append_output = NULL;
- q->build_output = NULL;
- q->force_output = NULL;
-
q->outstanding_bytes = 0;
q->empty = 1;
q->cork = 0;
}
/* Attempt to append this chunk to the packet. */
- status = (*q->append_output)(pkt, chunk);
+ status = sctp_packet_append_chunk(pkt, chunk);
switch (status) {
case SCTP_XMIT_PMTU_FULL:
/* Send this packet. */
- if ((error = (*q->force_output)(pkt)) == 0)
+ if ((error = sctp_packet_transmit(pkt)) == 0)
*start_timer = 1;
/* If we are retransmitting, we should only
case SCTP_XMIT_RWND_FULL:
/* Send this packet. */
- if ((error = (*q->force_output)(pkt)) == 0)
+ if ((error = sctp_packet_transmit(pkt)) == 0)
*start_timer = 1;
/* Stop sending DATA as there is no more room
lchunk = NULL;
break;
+ case SCTP_XMIT_NAGLE_DELAY:
+ /* Send this packet. */
+ if ((error = sctp_packet_transmit(pkt)) == 0)
+ *start_timer = 1;
+
+ /* Stop sending DATA because of nagle delay. */
+ list_add(lchunk, lqueue);
+ lchunk = NULL;
+ break;
+
default:
/* The append was successful, so add this chunk to
* the transmitted list.
struct sctp_packet *packet;
struct sctp_packet singleton;
struct sctp_association *asoc = q->asoc;
- int ecn_capable = asoc->peer.ecn_capable;
__u16 sport = asoc->base.bind_addr.port;
__u16 dport = asoc->peer.port;
__u32 vtag = asoc->peer.i.init_tag;
- /* This is the ECNE handler for singleton packets. */
- sctp_packet_phandler_t *s_ecne_handler = NULL;
- sctp_packet_phandler_t *ecne_handler = NULL;
struct sk_buff_head *queue;
struct sctp_transport *transport = NULL;
struct sctp_transport *new_transport;
* within a SCTP packet in increasing order of TSN.
* ...
*/
- if (ecn_capable) {
- s_ecne_handler = &sctp_get_no_prepend;
- ecne_handler = &sctp_get_ecne_prepend;
- }
queue = &q->control;
while ((chunk = (struct sctp_chunk *)skb_dequeue(queue))) {
&transport_list);
}
packet = &transport->packet;
- (*q->config_output)(packet, vtag,
- ecn_capable, ecne_handler);
+ sctp_packet_config(packet, vtag,
+ asoc->peer.ecn_capable);
}
switch (chunk->chunk_hdr->type) {
case SCTP_CID_INIT:
case SCTP_CID_INIT_ACK:
case SCTP_CID_SHUTDOWN_COMPLETE:
- (*q->init_output)(&singleton, transport, sport, dport);
- (*q->config_output)(&singleton, vtag, ecn_capable,
- s_ecne_handler);
- (void) (*q->build_output)(&singleton, chunk);
- error = (*q->force_output)(&singleton);
+ sctp_packet_init(&singleton, transport, sport, dport);
+ sctp_packet_config(&singleton, vtag, 0);
+ sctp_packet_append_chunk(&singleton, chunk);
+ error = sctp_packet_transmit(&singleton);
if (error < 0)
return error;
break;
case SCTP_CID_COOKIE_ACK:
case SCTP_CID_ECN_ECNE:
case SCTP_CID_ECN_CWR:
- (void) (*q->build_output)(packet, chunk);
- break;
-
case SCTP_CID_ASCONF:
case SCTP_CID_ASCONF_ACK:
- (void) (*q->build_output)(packet, chunk);
+ sctp_packet_transmit_chunk(packet, chunk);
break;
default:
}
packet = &transport->packet;
- (*q->config_output)(packet, vtag,
- ecn_capable, ecne_handler);
+ sctp_packet_config(packet, vtag,
+ asoc->peer.ecn_capable);
retran:
error = sctp_outq_flush_rtx(q, packet,
rtx_timeout, &start_timer);
}
packet = &transport->packet;
- (*q->config_output)(packet, vtag,
- ecn_capable, ecne_handler);
+ sctp_packet_config(packet, vtag,
+ asoc->peer.ecn_capable);
}
- SCTP_DEBUG_PRINTK("sctp_transmit_packet(%p, %p[%s]), ",
+ SCTP_DEBUG_PRINTK("sctp_outq_flush(%p, %p[%s]), ",
q, chunk,
chunk && chunk->chunk_hdr ?
sctp_cname(SCTP_ST_CHUNK(
atomic_read(&chunk->skb->users) : -1);
/* Add the chunk to the packet. */
- status = (*q->build_output)(packet, chunk);
+ status = sctp_packet_transmit_chunk(packet, chunk);
switch (status) {
case SCTP_XMIT_PMTU_FULL:
BUG();
}
- /* BUG: We assume that the (*q->force_output())
+ /* BUG: We assume that the sctp_packet_transmit()
* call below will succeed all the time and add the
* chunk to the transmitted list and restart the
* timers.
struct sctp_transport *t = list_entry(ltransport,
struct sctp_transport,
send_ready);
- if (t != transport)
- transport = t;
-
- packet = &transport->packet;
+ packet = &t->packet;
if (!sctp_packet_empty(packet))
- error = (*q->force_output)(packet);
+ error = sctp_packet_transmit(packet);
}
return error;
}
-/* Set the various output handling callbacks. */
-int sctp_outq_set_output_handlers(struct sctp_outq *q,
- sctp_outq_ohandler_init_t init,
- sctp_outq_ohandler_config_t config,
- sctp_outq_ohandler_t append,
- sctp_outq_ohandler_t build,
- sctp_outq_ohandler_force_t force)
-{
- q->init_output = init;
- q->config_output = config;
- q->append_output = append;
- q->build_output = build;
- q->force_output = force;
- return 0;
-}
-
/* Update unack_data based on the incoming SACK chunk */
static void sctp_sack_update_unack_data(struct sctp_association *assoc,
struct sctp_sackhdr *sack)
/* Make a transport for the bucket, Eliza... */
transport = sctp_transport_new(sctp_source(chunk), GFP_ATOMIC);
-
if (!transport)
goto nomem;
- /* Allocate a new packet for sending the response. */
- packet = t_new(struct sctp_packet, GFP_ATOMIC);
- if (!packet)
- goto nomem_packet;
-
/* Cache a route for the transport with the chunk's destination as
* the source address.
*/
sctp_transport_route(transport, (union sctp_addr *)&chunk->dest,
sctp_sk(sctp_get_ctl_sock()));
- packet = sctp_packet_init(packet, transport, sport, dport);
- packet = sctp_packet_config(packet, vtag, 0, NULL);
+ packet = sctp_packet_init(&transport->packet, transport, sport, dport);
+ packet = sctp_packet_config(packet, vtag, 0);
return packet;
-nomem_packet:
- sctp_transport_free(transport);
nomem:
return NULL;
}
void sctp_ootb_pkt_free(struct sctp_packet *packet)
{
sctp_transport_free(packet->transport);
- sctp_packet_free(packet);
}
/* Send a stale cookie error when a invalid COOKIE ECHO chunk is found */
INIT_LIST_HEAD(&peer->transmitted);
INIT_LIST_HEAD(&peer->send_ready);
INIT_LIST_HEAD(&peer->transports);
- sctp_packet_init(&peer->packet, peer, 0, 0);
/* Set up the retransmission timer. */
init_timer(&peer->T3_rtx_timer);
if (transport->asoc)
sctp_association_put(transport->asoc);
+ sctp_packet_free(&transport->packet);
+
dst_release(transport->dst);
kfree(transport);
SCTP_DBG_OBJCNT_DEC(transport);