#include <net/sctp/sctp.h>
#include <net/sctp/sm.h>
-static inline void sctp_ulpevent_set_owner(struct sctp_ulpevent *event,
- const struct sctp_association *asoc);
-static inline void sctp_ulpevent_release_owner(struct sctp_ulpevent *event);
static void sctp_ulpevent_receive_data(struct sctp_ulpevent *event,
struct sctp_association *asoc);
static void sctp_ulpevent_release_data(struct sctp_ulpevent *event);
+static void sctp_ulpevent_release_frag_data(struct sctp_ulpevent *event);
+
+
+/* Initialize an ULP event from an given skb. */
+SCTP_STATIC void sctp_ulpevent_init(struct sctp_ulpevent *event,
+ int msg_flags,
+ unsigned int len)
+{
+ memset(event, 0, sizeof(struct sctp_ulpevent));
+ event->msg_flags = msg_flags;
+ event->rmem_len = len;
+}
/* Create a new sctp_ulpevent. */
-struct sctp_ulpevent *sctp_ulpevent_new(int size, int msg_flags, int gfp)
+SCTP_STATIC struct sctp_ulpevent *sctp_ulpevent_new(int size, int msg_flags,
+ gfp_t gfp)
{
struct sctp_ulpevent *event;
struct sk_buff *skb;
goto fail;
event = sctp_skb2event(skb);
- sctp_ulpevent_init(event, msg_flags);
+ sctp_ulpevent_init(event, msg_flags, skb->truesize);
return event;
return NULL;
}
-/* Initialize an ULP event from an given skb. */
-void sctp_ulpevent_init(struct sctp_ulpevent *event, int msg_flags)
-{
- memset(event, 0, sizeof(struct sctp_ulpevent));
- event->msg_flags = msg_flags;
-}
-
/* Is this a MSG_NOTIFICATION? */
int sctp_ulpevent_is_notification(const struct sctp_ulpevent *event)
{
return MSG_NOTIFICATION == (event->msg_flags & MSG_NOTIFICATION);
}
+/* Hold the association in case the msg_name needs read out of
+ * the association.
+ */
+static inline void sctp_ulpevent_set_owner(struct sctp_ulpevent *event,
+ const struct sctp_association *asoc)
+{
+ struct sk_buff *skb;
+
+ /* Cast away the const, as we are just wanting to
+ * bump the reference count.
+ */
+ sctp_association_hold((struct sctp_association *)asoc);
+ skb = sctp_event2skb(event);
+ event->asoc = (struct sctp_association *)asoc;
+ atomic_add(event->rmem_len, &event->asoc->rmem_alloc);
+ sctp_skb_set_owner_r(skb, asoc->base.sk);
+}
+
+/* A simple destructor to give up the reference to the association. */
+static inline void sctp_ulpevent_release_owner(struct sctp_ulpevent *event)
+{
+ struct sctp_association *asoc = event->asoc;
+
+ atomic_sub(event->rmem_len, &asoc->rmem_alloc);
+ sctp_association_put(asoc);
+}
+
/* Create and initialize an SCTP_ASSOC_CHANGE event.
*
* 5.3.1.1 SCTP_ASSOC_CHANGE
struct sctp_ulpevent *sctp_ulpevent_make_assoc_change(
const struct sctp_association *asoc,
__u16 flags, __u16 state, __u16 error, __u16 outbound,
- __u16 inbound, int gfp)
+ __u16 inbound, gfp_t gfp)
{
struct sctp_ulpevent *event;
struct sctp_assoc_change *sac;
struct sctp_ulpevent *sctp_ulpevent_make_peer_addr_change(
const struct sctp_association *asoc,
const struct sockaddr_storage *aaddr,
- int flags, int state, int error, int gfp)
+ int flags, int state, int error, gfp_t gfp)
{
struct sctp_ulpevent *event;
struct sctp_paddr_change *spc;
*/
struct sctp_ulpevent *sctp_ulpevent_make_remote_error(
const struct sctp_association *asoc, struct sctp_chunk *chunk,
- __u16 flags, int gfp)
+ __u16 flags, gfp_t gfp)
{
struct sctp_ulpevent *event;
struct sctp_remote_error *sre;
struct sk_buff *skb;
sctp_errhdr_t *ch;
- __u16 cause;
+ __be16 cause;
int elen;
ch = (sctp_errhdr_t *)(chunk->skb->data);
/* Embed the event fields inside the cloned skb. */
event = sctp_skb2event(skb);
- sctp_ulpevent_init(event, MSG_NOTIFICATION);
+ sctp_ulpevent_init(event, MSG_NOTIFICATION, skb->truesize);
sre = (struct sctp_remote_error *)
skb_push(skb, sizeof(struct sctp_remote_error));
*/
struct sctp_ulpevent *sctp_ulpevent_make_send_failed(
const struct sctp_association *asoc, struct sctp_chunk *chunk,
- __u16 flags, __u32 error, int gfp)
+ __u16 flags, __u32 error, gfp_t gfp)
{
struct sctp_ulpevent *event;
struct sctp_send_failed *ssf;
/* Embed the event fields inside the cloned skb. */
event = sctp_skb2event(skb);
- sctp_ulpevent_init(event, MSG_NOTIFICATION);
+ sctp_ulpevent_init(event, MSG_NOTIFICATION, skb->truesize);
ssf = (struct sctp_send_failed *)
skb_push(skb, sizeof(struct sctp_send_failed));
*/
struct sctp_ulpevent *sctp_ulpevent_make_shutdown_event(
const struct sctp_association *asoc,
- __u16 flags, int gfp)
+ __u16 flags, gfp_t gfp)
{
struct sctp_ulpevent *event;
struct sctp_shutdown_event *sse;
struct sk_buff *skb;
- event = sctp_ulpevent_new(sizeof(struct sctp_assoc_change),
+ event = sctp_ulpevent_new(sizeof(struct sctp_shutdown_event),
MSG_NOTIFICATION, gfp);
if (!event)
goto fail;
return NULL;
}
+/* Create and initialize a SCTP_ADAPTATION_INDICATION notification.
+ *
+ * Socket Extensions for SCTP
+ * 5.3.1.6 SCTP_ADAPTATION_INDICATION
+ */
+struct sctp_ulpevent *sctp_ulpevent_make_adaptation_indication(
+ const struct sctp_association *asoc, gfp_t gfp)
+{
+ struct sctp_ulpevent *event;
+ struct sctp_adaptation_event *sai;
+ struct sk_buff *skb;
+
+ event = sctp_ulpevent_new(sizeof(struct sctp_adaptation_event),
+ MSG_NOTIFICATION, gfp);
+ if (!event)
+ goto fail;
+
+ skb = sctp_event2skb(event);
+ sai = (struct sctp_adaptation_event *)
+ skb_put(skb, sizeof(struct sctp_adaptation_event));
+
+ sai->sai_type = SCTP_ADAPTATION_INDICATION;
+ sai->sai_flags = 0;
+ sai->sai_length = sizeof(struct sctp_adaptation_event);
+ sai->sai_adaptation_ind = asoc->peer.adaptation_ind;
+ sctp_ulpevent_set_owner(event, asoc);
+ sai->sai_assoc_id = sctp_assoc2id(asoc);
+
+ return event;
+
+fail:
+ return NULL;
+}
+
/* A message has been received. Package this message as a notification
* to pass it to the upper layers. Go ahead and calculate the sndrcvinfo
* even if filtered out later.
*/
struct sctp_ulpevent *sctp_ulpevent_make_rcvmsg(struct sctp_association *asoc,
struct sctp_chunk *chunk,
- int gfp)
+ gfp_t gfp)
{
struct sctp_ulpevent *event = NULL;
struct sk_buff *skb;
/* Embed the event fields inside the cloned skb. */
event = sctp_skb2event(skb);
- /* Initialize event with flags 0. */
- sctp_ulpevent_init(event, 0);
+ /* Initialize event with flags 0 and correct length
+ * Since this is a clone of the original skb, only account for
+ * the data of this chunk as other chunks will be accounted separately.
+ */
+ sctp_ulpevent_init(event, 0, skb->len + sizeof(struct sk_buff));
sctp_ulpevent_receive_data(event, asoc);
event->ssn = ntohs(chunk->subh.data_hdr->ssn);
event->ppid = chunk->subh.data_hdr->ppid;
if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED) {
- event->flags |= MSG_UNORDERED;
+ event->flags |= SCTP_UNORDERED;
event->cumtsn = sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map);
}
event->tsn = ntohl(chunk->subh.data_hdr->tsn);
*
* 5.3.1.7 SCTP_PARTIAL_DELIVERY_EVENT
*
- * When a reciever is engaged in a partial delivery of a
- * message this notification will be used to inidicate
+ * When a receiver is engaged in a partial delivery of a
+ * message this notification will be used to indicate
* various events.
*/
struct sctp_ulpevent *sctp_ulpevent_make_pdapi(
- const struct sctp_association *asoc, __u32 indication, int gfp)
+ const struct sctp_association *asoc, __u32 indication,
+ gfp_t gfp)
{
struct sctp_ulpevent *event;
struct sctp_pdapi_event *pd;
struct sk_buff *skb;
- event = sctp_ulpevent_new(sizeof(struct sctp_assoc_change),
+ event = sctp_ulpevent_new(sizeof(struct sctp_pdapi_event),
MSG_NOTIFICATION, gfp);
if (!event)
goto fail;
*
* recvmsg() flags:
*
- * MSG_UNORDERED - This flag is present when the message was sent
+ * SCTP_UNORDERED - This flag is present when the message was sent
* non-ordered.
*/
sinfo.sinfo_flags = event->flags;
* This field will hold the current cumulative TSN as
* known by the underlying SCTP layer. Note this field is
* ignored when sending and only valid for a receive
- * operation when sinfo_flags are set to MSG_UNORDERED.
+ * operation when sinfo_flags are set to SCTP_UNORDERED.
*/
sinfo.sinfo_cumtsn = event->cumtsn;
/* sinfo_assoc_id: sizeof (sctp_assoc_t)
*/
sinfo.sinfo_assoc_id = sctp_assoc2id(event->asoc);
+ /* context value that is set via SCTP_CONTEXT socket option. */
+ sinfo.sinfo_context = event->asoc->default_rcv_context;
+
/* These fields are not used while receiving. */
- sinfo.sinfo_context = 0;
sinfo.sinfo_timetolive = 0;
put_cmsg(msghdr, IPPROTO_SCTP, SCTP_SNDRCV,
sizeof(struct sctp_sndrcvinfo), (void *)&sinfo);
}
-/* Stub skb destructor. */
-static void sctp_stub_rfree(struct sk_buff *skb)
-{
-/* WARNING: This function is just a warning not to use the
- * skb destructor. If the skb is shared, we may get the destructor
- * callback on some processor that does not own the sock_lock. This
- * was occuring with PACKET socket applications that were monitoring
- * our skbs. We can't take the sock_lock, because we can't risk
- * recursing if we do really own the sock lock. Instead, do all
- * of our rwnd manipulation while we own the sock_lock outright.
- */
-}
-
-/* Hold the association in case the msg_name needs read out of
- * the association.
- */
-static inline void sctp_ulpevent_set_owner(struct sctp_ulpevent *event,
- const struct sctp_association *asoc)
-{
- struct sk_buff *skb;
-
- /* Cast away the const, as we are just wanting to
- * bump the reference count.
- */
- sctp_association_hold((struct sctp_association *)asoc);
- skb = sctp_event2skb(event);
- skb->sk = asoc->base.sk;
- event->asoc = (struct sctp_association *)asoc;
- skb->destructor = sctp_stub_rfree;
-}
-
-/* A simple destructor to give up the reference to the association. */
-static inline void sctp_ulpevent_release_owner(struct sctp_ulpevent *event)
-{
- sctp_association_put(event->asoc);
-}
-
/* Do accounting for bytes received and hold a reference to the association
* for each skb.
*/
static void sctp_ulpevent_release_data(struct sctp_ulpevent *event)
{
struct sk_buff *skb, *frag;
+ unsigned int len;
/* Current stack structures assume that the rcv buffer is
* per socket. For UDP style sockets this is not true as
*/
skb = sctp_event2skb(event);
- sctp_assoc_rwnd_increase(event->asoc, skb_headlen(skb));
+ len = skb->len;
+
+ if (!skb->data_len)
+ goto done;
+
+ /* Don't forget the fragments. */
+ for (frag = skb_shinfo(skb)->frag_list; frag; frag = frag->next) {
+ /* NOTE: skb_shinfos are recursive. Although IP returns
+ * skb's with only 1 level of fragments, SCTP reassembly can
+ * increase the levels.
+ */
+ sctp_ulpevent_release_frag_data(sctp_skb2event(frag));
+ }
+
+done:
+ sctp_assoc_rwnd_increase(event->asoc, len);
+ sctp_ulpevent_release_owner(event);
+}
+
+static void sctp_ulpevent_release_frag_data(struct sctp_ulpevent *event)
+{
+ struct sk_buff *skb, *frag;
+
+ skb = sctp_event2skb(event);
if (!skb->data_len)
goto done;
* skb's with only 1 level of fragments, SCTP reassembly can
* increase the levels.
*/
- sctp_ulpevent_release_data(sctp_skb2event(frag));
+ sctp_ulpevent_release_frag_data(sctp_skb2event(frag));
}
done:
/* Free a ulpevent that has an owner. It includes releasing the reference
* to the owner, updating the rwnd in case of a DATA event and freeing the
* skb.
- * See comments in sctp_stub_rfree().
*/
void sctp_ulpevent_free(struct sctp_ulpevent *event)
{