Commit fe276637 authored by Sridhar Samudrala's avatar Sridhar Samudrala

[SCTP] Avoid the use of constant SCTP_IP_OVERHEAD to determine the

max data size in a SCTP packet.

Calculate the overhead based on the socket's protocol family header
length.
parent 321fe6f8
...@@ -57,15 +57,6 @@ enum { SCTP_MAX_STREAM = 0xffff }; ...@@ -57,15 +57,6 @@ enum { SCTP_MAX_STREAM = 0xffff };
enum { SCTP_DEFAULT_OUTSTREAMS = 10 }; enum { SCTP_DEFAULT_OUTSTREAMS = 10 };
enum { SCTP_DEFAULT_INSTREAMS = SCTP_MAX_STREAM }; enum { SCTP_DEFAULT_INSTREAMS = SCTP_MAX_STREAM };
/* Define the amount of space to reserve for SCTP, IP, LL.
* There is a little bit of waste that we are always allocating
* for ipv6 headers, but this seems worth the simplicity.
*/
#define SCTP_IP_OVERHEAD ((sizeof(struct sctphdr)\
+ sizeof(struct ipv6hdr)\
+ MAX_HEADER))
/* Since CIDs are sparse, we need all four of the following /* Since CIDs are sparse, we need all four of the following
* symbols. CIDs are dense through SCTP_CID_BASE_MAX. * symbols. CIDs are dense through SCTP_CID_BASE_MAX.
*/ */
......
...@@ -437,12 +437,15 @@ static inline __s32 sctp_jitter(__u32 rto) ...@@ -437,12 +437,15 @@ static inline __s32 sctp_jitter(__u32 rto)
static inline int sctp_frag_point(const struct sctp_opt *sp, int pmtu) static inline int sctp_frag_point(const struct sctp_opt *sp, int pmtu)
{ {
int frag = pmtu; int frag = pmtu;
frag -= SCTP_IP_OVERHEAD + sizeof(struct sctp_data_chunk);
frag -= sizeof(struct sctp_sack_chunk); frag -= sp->pf->af->net_header_len;
frag -= sizeof(struct sctphdr) + sizeof(struct sctp_data_chunk);
if (sp->user_frag) if (sp->user_frag)
frag = min_t(int, frag, sp->user_frag); frag = min_t(int, frag, sp->user_frag);
frag = min_t(int, frag, SCTP_MAX_CHUNK_LEN);
return frag; return frag;
} }
......
...@@ -667,6 +667,9 @@ struct sctp_packet { ...@@ -667,6 +667,9 @@ struct sctp_packet {
/* This contains the payload chunks. */ /* This contains the payload chunks. */
struct sk_buff_head chunks; struct sk_buff_head chunks;
/* This is the overhead of the sctp and ip headers. */
size_t overhead;
/* This is the total size of all chunks INCLUDING padding. */ /* This is the total size of all chunks INCLUDING padding. */
size_t size; size_t size;
...@@ -721,6 +724,10 @@ sctp_outq_ohandler_t sctp_packet_transmit_chunk; ...@@ -721,6 +724,10 @@ sctp_outq_ohandler_t sctp_packet_transmit_chunk;
sctp_outq_ohandler_force_t sctp_packet_transmit; sctp_outq_ohandler_force_t sctp_packet_transmit;
void sctp_packet_free(struct sctp_packet *); void sctp_packet_free(struct sctp_packet *);
static inline int sctp_packet_empty(struct sctp_packet *packet)
{
return (packet->size == packet->overhead);
}
/* This represents a remote transport address. /* This represents a remote transport address.
* For local transport addresses, we just use union sctp_addr. * For local transport addresses, we just use union sctp_addr.
......
...@@ -192,24 +192,9 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc, ...@@ -192,24 +192,9 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc,
msg->can_expire = 1; msg->can_expire = 1;
} }
/* What is a reasonable fragmentation point right now? */ max = asoc->frag_point;
max = asoc->pmtu;
if (max < SCTP_MIN_PMTU)
max = SCTP_MIN_PMTU;
max -= SCTP_IP_OVERHEAD;
/* Make sure not beyond maximum chunk size. */
if (max > SCTP_MAX_CHUNK_LEN)
max = SCTP_MAX_CHUNK_LEN;
/* Subtract out the overhead of a data chunk header. */
max -= sizeof(struct sctp_data_chunk);
whole = 0;
/* If user has specified smaller fragmentation, make it so. */
if (sctp_sk(asoc->base.sk)->user_frag)
max = min_t(int, max, sctp_sk(asoc->base.sk)->user_frag);
whole = 0;
first_len = max; first_len = max;
/* Encourage Cookie-ECHO bundling. */ /* Encourage Cookie-ECHO bundling. */
......
...@@ -73,8 +73,6 @@ struct sctp_packet *sctp_packet_config(struct sctp_packet *packet, ...@@ -73,8 +73,6 @@ struct sctp_packet *sctp_packet_config(struct sctp_packet *packet,
__u32 vtag, int ecn_capable, __u32 vtag, int ecn_capable,
sctp_packet_phandler_t *prepend_handler) sctp_packet_phandler_t *prepend_handler)
{ {
int packet_empty = (packet->size == SCTP_IP_OVERHEAD);
packet->vtag = vtag; packet->vtag = vtag;
packet->ecn_capable = ecn_capable; packet->ecn_capable = ecn_capable;
packet->get_prepend_chunk = prepend_handler; packet->get_prepend_chunk = prepend_handler;
...@@ -83,7 +81,7 @@ struct sctp_packet *sctp_packet_config(struct sctp_packet *packet, ...@@ -83,7 +81,7 @@ struct sctp_packet *sctp_packet_config(struct sctp_packet *packet,
packet->ipfragok = 0; packet->ipfragok = 0;
/* We might need to call the prepend_handler right away. */ /* We might need to call the prepend_handler right away. */
if (packet_empty) if (sctp_packet_empty(packet))
sctp_packet_reset(packet); sctp_packet_reset(packet);
return packet; return packet;
} }
...@@ -93,11 +91,22 @@ struct sctp_packet *sctp_packet_init(struct sctp_packet *packet, ...@@ -93,11 +91,22 @@ struct sctp_packet *sctp_packet_init(struct sctp_packet *packet,
struct sctp_transport *transport, struct sctp_transport *transport,
__u16 sport, __u16 dport) __u16 sport, __u16 dport)
{ {
struct sctp_association *asoc = transport->asoc;
size_t overhead;
packet->transport = transport; packet->transport = transport;
packet->source_port = sport; packet->source_port = sport;
packet->destination_port = dport; packet->destination_port = dport;
skb_queue_head_init(&packet->chunks); skb_queue_head_init(&packet->chunks);
packet->size = SCTP_IP_OVERHEAD; if (asoc) {
struct sctp_opt *sp = sctp_sk(asoc->base.sk);
overhead = sp->pf->af->net_header_len;
} else {
overhead = sizeof(struct ipv6hdr);
}
overhead += sizeof(struct sctphdr);
packet->overhead = overhead;
packet->size = overhead;
packet->vtag = 0; packet->vtag = 0;
packet->ecn_capable = 0; packet->ecn_capable = 0;
packet->get_prepend_chunk = NULL; packet->get_prepend_chunk = NULL;
...@@ -215,17 +224,14 @@ sctp_xmit_t sctp_packet_append_chunk(struct sctp_packet *packet, ...@@ -215,17 +224,14 @@ sctp_xmit_t sctp_packet_append_chunk(struct sctp_packet *packet,
/* Decide if we need to fragment or resubmit later. */ /* Decide if we need to fragment or resubmit later. */
if (too_big) { if (too_big) {
int packet_empty = (packet->size == SCTP_IP_OVERHEAD);
/* Both control chunks and data chunks with TSNs are /* Both control chunks and data chunks with TSNs are
* non-fragmentable. * non-fragmentable.
*/ */
if (packet_empty || !sctp_chunk_is_data(chunk)) { if (sctp_packet_empty(packet) || !sctp_chunk_is_data(chunk)) {
/* We no longer do re-fragmentation. /* We no longer do re-fragmentation.
* Just fragment at the IP layer, if we * Just fragment at the IP layer, if we
* actually hit this condition * actually hit this condition
*/ */
packet->ipfragok = 1; packet->ipfragok = 1;
goto append; goto append;
...@@ -297,7 +303,7 @@ int sctp_packet_transmit(struct sctp_packet *packet) ...@@ -297,7 +303,7 @@ int sctp_packet_transmit(struct sctp_packet *packet)
goto nomem; goto nomem;
/* Make sure the outbound skb has enough header room reserved. */ /* Make sure the outbound skb has enough header room reserved. */
skb_reserve(nskb, SCTP_IP_OVERHEAD); skb_reserve(nskb, packet->overhead);
/* Set the owning socket so that we know where to get the /* Set the owning socket so that we know where to get the
* destination IP address. * destination IP address.
...@@ -471,7 +477,7 @@ int sctp_packet_transmit(struct sctp_packet *packet) ...@@ -471,7 +477,7 @@ int sctp_packet_transmit(struct sctp_packet *packet)
(*tp->af_specific->sctp_xmit)(nskb, tp, packet->ipfragok); (*tp->af_specific->sctp_xmit)(nskb, tp, packet->ipfragok);
out: out:
packet->size = SCTP_IP_OVERHEAD; packet->size = packet->overhead;
return err; return err;
no_route: no_route:
kfree_skb(nskb); kfree_skb(nskb);
...@@ -497,7 +503,6 @@ int sctp_packet_transmit(struct sctp_packet *packet) ...@@ -497,7 +503,6 @@ int sctp_packet_transmit(struct sctp_packet *packet)
goto out; goto out;
nomem: nomem:
err = -ENOMEM; err = -ENOMEM;
printk("%s alloc_skb failed.\n", __FUNCTION__);
goto err; goto err;
} }
...@@ -512,7 +517,7 @@ static void sctp_packet_reset(struct sctp_packet *packet) ...@@ -512,7 +517,7 @@ static void sctp_packet_reset(struct sctp_packet *packet)
{ {
struct sctp_chunk *chunk = NULL; struct sctp_chunk *chunk = NULL;
packet->size = SCTP_IP_OVERHEAD; packet->size = packet->overhead;
if (packet->get_prepend_chunk) if (packet->get_prepend_chunk)
chunk = packet->get_prepend_chunk(packet->transport->asoc); chunk = packet->get_prepend_chunk(packet->transport->asoc);
...@@ -609,7 +614,7 @@ static sctp_xmit_t sctp_packet_append_data(struct sctp_packet *packet, ...@@ -609,7 +614,7 @@ static sctp_xmit_t sctp_packet_append_data(struct sctp_packet *packet,
* if any previously transmitted data on the connection remains * if any previously transmitted data on the connection remains
* unacknowledged. * unacknowledged.
*/ */
if (!sp->nodelay && SCTP_IP_OVERHEAD == packet->size && if (!sp->nodelay && sctp_packet_empty(packet) &&
q->outstanding_bytes && sctp_state(asoc, ESTABLISHED)) { q->outstanding_bytes && sctp_state(asoc, ESTABLISHED)) {
unsigned len = datasize + q->out_qlen; unsigned len = datasize + q->out_qlen;
...@@ -617,7 +622,7 @@ static sctp_xmit_t sctp_packet_append_data(struct sctp_packet *packet, ...@@ -617,7 +622,7 @@ static sctp_xmit_t sctp_packet_append_data(struct sctp_packet *packet,
* data will fit or delay in hopes of bundling a full * data will fit or delay in hopes of bundling a full
* sized packet. * sized packet.
*/ */
if (len < asoc->pmtu - SCTP_IP_OVERHEAD) { if (len < asoc->pmtu - packet->overhead) {
retval = SCTP_XMIT_NAGLE_DELAY; retval = SCTP_XMIT_NAGLE_DELAY;
goto finish; goto finish;
} }
......
...@@ -926,7 +926,7 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout) ...@@ -926,7 +926,7 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
transport = t; transport = t;
packet = &transport->packet; packet = &transport->packet;
if (packet->size != SCTP_IP_OVERHEAD) if (!sctp_packet_empty(packet))
error = (*q->force_output)(packet); error = (*q->force_output)(packet);
} }
......
...@@ -1948,6 +1948,9 @@ static int sctp_setsockopt_mappedv4(struct sock *sk, char *optval, int optlen) ...@@ -1948,6 +1948,9 @@ static int sctp_setsockopt_mappedv4(struct sock *sk, char *optval, int optlen)
*/ */
static int sctp_setsockopt_maxseg(struct sock *sk, char *optval, int optlen) static int sctp_setsockopt_maxseg(struct sock *sk, char *optval, int optlen)
{ {
struct sctp_association *asoc;
struct list_head *pos;
struct sctp_opt *sp = sctp_sk(sk);
int val; int val;
if (optlen < sizeof(int)) if (optlen < sizeof(int))
...@@ -1956,7 +1959,15 @@ static int sctp_setsockopt_maxseg(struct sock *sk, char *optval, int optlen) ...@@ -1956,7 +1959,15 @@ static int sctp_setsockopt_maxseg(struct sock *sk, char *optval, int optlen)
return -EFAULT; return -EFAULT;
if ((val < 8) || (val > SCTP_MAX_CHUNK_LEN)) if ((val < 8) || (val > SCTP_MAX_CHUNK_LEN))
return -EINVAL; return -EINVAL;
sctp_sk(sk)->user_frag = val; sp->user_frag = val;
if (val) {
/* Update the frag_point of the existing associations. */
list_for_each(pos, &(sp->ep->asocs)) {
asoc = list_entry(pos, struct sctp_association, asocs);
asoc->frag_point = sctp_frag_point(sp, asoc->pmtu);
}
}
return 0; return 0;
} }
...@@ -2531,10 +2542,6 @@ static int sctp_getsockopt_sctp_status(struct sock *sk, int len, char *optval, ...@@ -2531,10 +2542,6 @@ static int sctp_getsockopt_sctp_status(struct sock *sk, int len, char *optval,
status.sstat_penddata = sctp_tsnmap_pending(&asoc->peer.tsn_map); status.sstat_penddata = sctp_tsnmap_pending(&asoc->peer.tsn_map);
status.sstat_instrms = asoc->c.sinit_max_instreams; status.sstat_instrms = asoc->c.sinit_max_instreams;
status.sstat_outstrms = asoc->c.sinit_num_ostreams; status.sstat_outstrms = asoc->c.sinit_num_ostreams;
/* Just in time frag_point update. */
if (sctp_sk(sk)->user_frag)
asoc->frag_point
= min_t(int, asoc->frag_point, sctp_sk(sk)->user_frag);
status.sstat_fragmentation_point = asoc->frag_point; status.sstat_fragmentation_point = asoc->frag_point;
status.sstat_primary.spinfo_assoc_id = sctp_assoc2id(transport->asoc); status.sstat_primary.spinfo_assoc_id = sctp_assoc2id(transport->asoc);
memcpy(&status.sstat_primary.spinfo_address, memcpy(&status.sstat_primary.spinfo_address,
......
...@@ -839,6 +839,9 @@ static void sctp_ulpevent_receive_data(struct sctp_ulpevent *event, ...@@ -839,6 +839,9 @@ static void sctp_ulpevent_receive_data(struct sctp_ulpevent *event,
sctp_ulpevent_set_owner(event, asoc); sctp_ulpevent_set_owner(event, asoc);
sctp_assoc_rwnd_decrease(asoc, skb_headlen(skb)); sctp_assoc_rwnd_decrease(asoc, skb_headlen(skb));
if (!skb->data_len)
return;
/* Note: Not clearing the entire event struct as this is just a /* Note: Not clearing the entire event struct as this is just a
* fragment of the real event. However, we still need to do rwnd * fragment of the real event. However, we still need to do rwnd
* accounting. * accounting.
...@@ -867,6 +870,9 @@ static void sctp_ulpevent_release_data(struct sctp_ulpevent *event) ...@@ -867,6 +870,9 @@ static void sctp_ulpevent_release_data(struct sctp_ulpevent *event)
skb = sctp_event2skb(event); skb = sctp_event2skb(event);
sctp_assoc_rwnd_increase(event->asoc, skb_headlen(skb)); sctp_assoc_rwnd_increase(event->asoc, skb_headlen(skb));
if (!skb->data_len)
goto done;
/* Don't forget the fragments. */ /* Don't forget the fragments. */
for (frag = skb_shinfo(skb)->frag_list; frag; frag = frag->next) { for (frag = skb_shinfo(skb)->frag_list; frag; frag = frag->next) {
/* NOTE: skb_shinfos are recursive. Although IP returns /* NOTE: skb_shinfos are recursive. Although IP returns
...@@ -875,6 +881,8 @@ static void sctp_ulpevent_release_data(struct sctp_ulpevent *event) ...@@ -875,6 +881,8 @@ static void sctp_ulpevent_release_data(struct sctp_ulpevent *event)
*/ */
sctp_ulpevent_release_data(sctp_skb2event(frag)); sctp_ulpevent_release_data(sctp_skb2event(frag));
} }
done:
sctp_ulpevent_release_owner(event); sctp_ulpevent_release_owner(event);
} }
......
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