mac80211: document ieee80211_rx() context requirement
[safe/jmp/linux-2.6] / net / sctp / ulpevent.c
index ba97f97..8b3560f 100644 (file)
@@ -1,4 +1,4 @@
-/* SCTP kernel reference Implementation
+/* SCTP kernel implementation
  * (C) Copyright IBM Corp. 2001, 2004
  * Copyright (c) 1999-2000 Cisco, Inc.
  * Copyright (c) 1999-2001 Motorola, Inc.
@@ -8,13 +8,14 @@
  *
  * These functions manipulate an sctp event.   The struct ulpevent is used
  * to carry notifications and data to the ULP (sockets).
- * The SCTP reference implementation is free software;
+ *
+ * This SCTP implementation is free software;
  * you can redistribute it and/or modify it under the terms of
  * the GNU General Public License as published by
  * the Free Software Foundation; either version 2, or (at your option)
  * any later version.
  *
- * The SCTP reference implementation is distributed in the hope that it
+ * This SCTP implementation is distributed in the hope that it
  * will be useful, but WITHOUT ANY WARRANTY; without even the implied
  *                 ************************
  * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 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)
+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.  */
@@ -71,7 +77,7 @@ SCTP_STATIC struct sctp_ulpevent *sctp_ulpevent_new(int size, int msg_flags,
                goto fail;
 
        event = sctp_skb2event(skb);
-       sctp_ulpevent_init(event, msg_flags);
+       sctp_ulpevent_init(event, msg_flags, skb->truesize);
 
        return event;
 
@@ -99,17 +105,16 @@ static inline void sctp_ulpevent_set_owner(struct sctp_ulpevent *event,
        sctp_association_hold((struct sctp_association *)asoc);
        skb = sctp_event2skb(event);
        event->asoc = (struct sctp_association *)asoc;
-       atomic_add(skb->truesize, &event->asoc->rmem_alloc);
-       skb_set_owner_r(skb, asoc->base.sk);
+       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;
-       struct sk_buff *skb = sctp_event2skb(event);
 
-       atomic_sub(skb->truesize, &asoc->rmem_alloc);
+       atomic_sub(event->rmem_len, &asoc->rmem_alloc);
        sctp_association_put(asoc);
 }
 
@@ -127,19 +132,47 @@ static inline void sctp_ulpevent_release_owner(struct sctp_ulpevent *event)
 struct sctp_ulpevent  *sctp_ulpevent_make_assoc_change(
        const struct sctp_association *asoc,
        __u16 flags, __u16 state, __u16 error, __u16 outbound,
-       __u16 inbound, gfp_t gfp)
+       __u16 inbound, struct sctp_chunk *chunk, gfp_t gfp)
 {
        struct sctp_ulpevent *event;
        struct sctp_assoc_change *sac;
        struct sk_buff *skb;
 
-       event = sctp_ulpevent_new(sizeof(struct sctp_assoc_change),
+       /* If the lower layer passed in the chunk, it will be
+        * an ABORT, so we need to include it in the sac_info.
+        */
+       if (chunk) {
+               /* Copy the chunk data to a new skb and reserve enough
+                * head room to use as notification.
+                */
+               skb = skb_copy_expand(chunk->skb,
+                                     sizeof(struct sctp_assoc_change), 0, gfp);
+
+               if (!skb)
+                       goto fail;
+
+               /* Embed the event fields inside the cloned skb.  */
+               event = sctp_skb2event(skb);
+               sctp_ulpevent_init(event, MSG_NOTIFICATION, skb->truesize);
+
+               /* Include the notification structure */
+               sac = (struct sctp_assoc_change *)
+                       skb_push(skb, sizeof(struct sctp_assoc_change));
+
+               /* Trim the buffer to the right length.  */
+               skb_trim(skb, sizeof(struct sctp_assoc_change) +
+                        ntohs(chunk->chunk_hdr->length) -
+                        sizeof(sctp_chunkhdr_t));
+       } else {
+               event = sctp_ulpevent_new(sizeof(struct sctp_assoc_change),
                                  MSG_NOTIFICATION, gfp);
-       if (!event)
-               goto fail;
-       skb = sctp_event2skb(event);
-       sac = (struct sctp_assoc_change *)
-               skb_put(skb, sizeof(struct sctp_assoc_change));
+               if (!event)
+                       goto fail;
+
+               skb = sctp_event2skb(event);
+               sac = (struct sctp_assoc_change *) skb_put(skb,
+                                       sizeof(struct sctp_assoc_change));
+       }
 
        /* Socket Extensions for SCTP
         * 5.3.1.1 SCTP_ASSOC_CHANGE
@@ -173,7 +206,7 @@ struct sctp_ulpevent  *sctp_ulpevent_make_assoc_change(
         * This field is the total length of the notification data, including
         * the notification header.
         */
-       sac->sac_length = sizeof(struct sctp_assoc_change);
+       sac->sac_length = skb->len;
 
        /* Socket Extensions for SCTP
         * 5.3.1.1 SCTP_ASSOC_CHANGE
@@ -347,7 +380,7 @@ struct sctp_ulpevent *sctp_ulpevent_make_remote_error(
        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);
@@ -370,7 +403,7 @@ struct sctp_ulpevent *sctp_ulpevent_make_remote_error(
 
        /* 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));
@@ -462,7 +495,7 @@ struct sctp_ulpevent *sctp_ulpevent_make_send_failed(
 
        /* 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));
@@ -605,31 +638,31 @@ fail:
        return NULL;
 }
 
-/* Create and initialize a SCTP_ADAPTION_INDICATION notification.
+/* Create and initialize a SCTP_ADAPTATION_INDICATION notification.
  *
  * Socket Extensions for SCTP
- * 5.3.1.6 SCTP_ADAPTION_INDICATION
+ * 5.3.1.6 SCTP_ADAPTATION_INDICATION
  */
-struct sctp_ulpevent *sctp_ulpevent_make_adaption_indication(
+struct sctp_ulpevent *sctp_ulpevent_make_adaptation_indication(
        const struct sctp_association *asoc, gfp_t gfp)
 {
        struct sctp_ulpevent *event;
-       struct sctp_adaption_event *sai;
+       struct sctp_adaptation_event *sai;
        struct sk_buff *skb;
 
-       event = sctp_ulpevent_new(sizeof(struct sctp_adaption_event),
+       event = sctp_ulpevent_new(sizeof(struct sctp_adaptation_event),
                                  MSG_NOTIFICATION, gfp);
        if (!event)
                goto fail;
 
        skb = sctp_event2skb(event);
-       sai = (struct sctp_adaption_event *)
-               skb_put(skb, sizeof(struct sctp_adaption_event));
+       sai = (struct sctp_adaptation_event *)
+               skb_put(skb, sizeof(struct sctp_adaptation_event));
 
-       sai->sai_type = SCTP_ADAPTION_INDICATION;
+       sai->sai_type = SCTP_ADAPTATION_INDICATION;
        sai->sai_flags = 0;
-       sai->sai_length = sizeof(struct sctp_adaption_event);
-       sai->sai_adaption_ind = asoc->peer.adaption_ind;
+       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);
 
@@ -653,12 +686,37 @@ struct sctp_ulpevent *sctp_ulpevent_make_rcvmsg(struct sctp_association *asoc,
        struct sctp_ulpevent *event = NULL;
        struct sk_buff *skb;
        size_t padding, len;
+       int rx_count;
+
+       /*
+        * check to see if we need to make space for this
+        * new skb, expand the rcvbuffer if needed, or drop
+        * the frame
+        */
+       if (asoc->ep->rcvbuf_policy)
+               rx_count = atomic_read(&asoc->rmem_alloc);
+       else
+               rx_count = atomic_read(&asoc->base.sk->sk_rmem_alloc);
+
+       if (rx_count >= asoc->base.sk->sk_rcvbuf) {
+
+               if ((asoc->base.sk->sk_userlocks & SOCK_RCVBUF_LOCK) ||
+                   (!sk_rmem_schedule(asoc->base.sk, chunk->skb->truesize)))
+                       goto fail;
+       }
 
        /* Clone the original skb, sharing the data.  */
        skb = skb_clone(chunk->skb, gfp);
        if (!skb)
                goto fail;
 
+       /* Now that all memory allocations for this chunk succeeded, we
+        * can mark it as received so the tsn_map is updated correctly.
+        */
+       if (sctp_tsnmap_mark(&asoc->peer.tsn_map,
+                            ntohl(chunk->subh.data_hdr->tsn)))
+               goto fail_mark;
+
        /* First calculate the padding, so we don't inadvertently
         * pass up the wrong length to the user.
         *
@@ -680,8 +738,11 @@ struct sctp_ulpevent *sctp_ulpevent_make_rcvmsg(struct sctp_association *asoc,
        /* 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);
 
@@ -696,8 +757,12 @@ struct sctp_ulpevent *sctp_ulpevent_make_rcvmsg(struct sctp_association *asoc,
        event->msg_flags |= chunk->chunk_hdr->flags;
        event->iif = sctp_chunk_iif(chunk);
 
-fail:
        return event;
+
+fail_mark:
+       kfree_skb(skb);
+fail:
+       return NULL;
 }
 
 /* Create a partial delivery related event.
@@ -742,7 +807,7 @@ struct sctp_ulpevent *sctp_ulpevent_make_pdapi(
         */
        pd->pdapi_length = sizeof(struct sctp_pdapi_event);
 
-        /*  pdapi_indication: 32 bits (unsigned integer)
+       /*  pdapi_indication: 32 bits (unsigned integer)
         *
         * This field holds the indication being sent to the application.
         */
@@ -760,6 +825,43 @@ fail:
        return NULL;
 }
 
+struct sctp_ulpevent *sctp_ulpevent_make_authkey(
+       const struct sctp_association *asoc, __u16 key_id,
+       __u32 indication, gfp_t gfp)
+{
+       struct sctp_ulpevent *event;
+       struct sctp_authkey_event *ak;
+       struct sk_buff *skb;
+
+       event = sctp_ulpevent_new(sizeof(struct sctp_authkey_event),
+                                 MSG_NOTIFICATION, gfp);
+       if (!event)
+               goto fail;
+
+       skb = sctp_event2skb(event);
+       ak = (struct sctp_authkey_event *)
+               skb_put(skb, sizeof(struct sctp_authkey_event));
+
+       ak->auth_type = SCTP_AUTHENTICATION_INDICATION;
+       ak->auth_flags = 0;
+       ak->auth_length = sizeof(struct sctp_authkey_event);
+
+       ak->auth_keynumber = key_id;
+       ak->auth_altkeynumber = 0;
+       ak->auth_indication = indication;
+
+       /*
+        * The association id field, holds the identifier for the association.
+        */
+       sctp_ulpevent_set_owner(event, asoc);
+       ak->auth_assoc_id = sctp_assoc2id(asoc);
+
+       return event;
+fail:
+       return NULL;
+}
+
+
 /* Return the notification type, assuming this is a notification
  * event.
  */
@@ -768,7 +870,7 @@ __u16 sctp_ulpevent_get_notification_type(const struct sctp_ulpevent *event)
        union sctp_notification *notification;
        struct sk_buff *skb;
 
-       skb = sctp_event2skb((struct sctp_ulpevent *)event);
+       skb = sctp_event2skb(event);
        notification = (union sctp_notification *) skb->data;
        return notification->sn_header.sn_type;
 }
@@ -783,13 +885,13 @@ void sctp_ulpevent_read_sndrcvinfo(const struct sctp_ulpevent *event,
                return;
 
        /* Sockets API Extensions for SCTP
-        * Section 5.2.2 SCTP Header Information Structure (SCTP_SNDRCV)
-        *
-        * sinfo_stream: 16 bits (unsigned integer)
-        *
-        * For recvmsg() the SCTP stack places the message's stream number in
-        * this value.
-       */
+        * Section 5.2.2 SCTP Header Information Structure (SCTP_SNDRCV)
+        *
+        * sinfo_stream: 16 bits (unsigned integer)
+        *
+        * For recvmsg() the SCTP stack places the message's stream number in
+        * this value.
+       */
        sinfo.sinfo_stream = event->stream;
        /* sinfo_ssn: 16 bits (unsigned integer)
         *
@@ -821,7 +923,7 @@ void sctp_ulpevent_read_sndrcvinfo(const struct sctp_ulpevent *event,
        sinfo.sinfo_flags = event->flags;
        /* sinfo_tsn: 32 bit (unsigned integer)
         *
-        * For the receiving side, this field holds a TSN that was 
+        * For the receiving side, this field holds a TSN that was
         * assigned to one of the SCTP Data Chunks.
         */
        sinfo.sinfo_tsn = event->tsn;
@@ -842,8 +944,10 @@ void sctp_ulpevent_read_sndrcvinfo(const struct sctp_ulpevent *event,
         */
        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,
@@ -870,19 +974,19 @@ static void sctp_ulpevent_receive_data(struct sctp_ulpevent *event,
         * fragment of the real event.  However, we still need to do rwnd
         * accounting.
         * In general, the skb passed from IP can have only 1 level of
-        * fragments. But we allow multiple levels of fragments. 
+        * fragments. But we allow multiple levels of fragments.
         */
-       for (frag = skb_shinfo(skb)->frag_list; frag; frag = frag->next) {
+       skb_walk_frags(skb, frag)
                sctp_ulpevent_receive_data(sctp_skb2event(frag), asoc);
-       }
 }
 
 /* Do accounting for bytes just read by user and release the references to
  * the association.
- */ 
+ */
 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
@@ -892,18 +996,41 @@ static void sctp_ulpevent_release_data(struct sctp_ulpevent *event)
         */
 
        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. */
+       skb_walk_frags(skb, frag) {
+               /* 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;
 
        /* Don't forget the fragments. */
-       for (frag = skb_shinfo(skb)->frag_list; frag; frag = frag->next) {
+       skb_walk_frags(skb, frag) {
                /* 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_data(sctp_skb2event(frag));
+               sctp_ulpevent_release_frag_data(sctp_skb2event(frag));
        }
 
 done: