struct sctp_endpoint;
struct sctp_association;
struct sctp_authkey;
+struct sctp_hmacalgo;
/*
* Define a generic struct that will hold all the info
void sctp_auth_calculate_hmac(const struct sctp_association *asoc,
struct sk_buff *skb,
struct sctp_auth_chunk *auth, gfp_t gfp);
+
+/* API Helpers */
+int sctp_auth_ep_add_chunkid(struct sctp_endpoint *ep, __u8 chunk_id);
+int sctp_auth_ep_set_hmacs(struct sctp_endpoint *ep,
+ struct sctp_hmacalgo *hmacs);
+int sctp_auth_set_key(struct sctp_endpoint *ep,
+ struct sctp_association *asoc,
+ struct sctp_authkey *auth_key);
+int sctp_auth_set_active_key(struct sctp_endpoint *ep,
+ struct sctp_association *asoc,
+ __u16 key_id);
+int sctp_auth_del_key_id(struct sctp_endpoint *ep,
+ struct sctp_association *asoc,
+ __u16 key_id);
+
#endif
struct sctp_chunk *chunk,
gfp_t gfp);
+struct sctp_ulpevent *sctp_ulpevent_make_authkey(
+ const struct sctp_association *asoc, __u16 key_id,
+ __u32 indication, gfp_t gfp);
+
void sctp_ulpevent_read_sndrcvinfo(const struct sctp_ulpevent *event,
struct msghdr *);
__u16 sctp_ulpevent_get_notification_type(const struct sctp_ulpevent *event);
#define SCTP_PARTIAL_DELIVERY_POINT SCTP_PARTIAL_DELIVERY_POINT
SCTP_MAX_BURST, /* Set/Get max burst */
#define SCTP_MAX_BURST SCTP_MAX_BURST
+ SCTP_AUTH_CHUNK, /* Set only: add a chunk type to authenticat */
+#define SCTP_AUTH_CHUNK SCTP_AUTH_CHUNK
+ SCTP_HMAC_IDENT,
+#define SCTP_HMAC_IDENT SCTP_HMAC_IDENT
+ SCTP_AUTH_KEY,
+#define SCTP_AUTH_KEY SCTP_AUTH_KEY
+ SCTP_AUTH_ACTIVE_KEY,
+#define SCTP_AUTH_ACTIVE_KEY SCTP_AUTH_ACTIVE_KEY
+ SCTP_AUTH_DELETE_KEY,
+#define SCTP_AUTH_DELETE_KEY SCTP_AUTH_DELETE_KEY
+ SCTP_PEER_AUTH_CHUNKS, /* Read only */
+#define SCTP_PEER_AUTH_CHUNKS SCTP_PEER_AUTH_CHUNKS
+ SCTP_LOCAL_AUTH_CHUNKS, /* Read only */
+#define SCTP_LOCAL_AUTH_CHUNKS SCTP_LOCAL_AUTH_CHUNKS
+
/* Internal Socket Options. Some of the sctp library functions are
* implemented using these socket options.
enum { SCTP_PARTIAL_DELIVERY_ABORTED=0, };
+struct sctp_authkey_event {
+ __u16 auth_type;
+ __u16 auth_flags;
+ __u32 auth_length;
+ __u16 auth_keynumber;
+ __u16 auth_altkeynumber;
+ __u32 auth_indication;
+ sctp_assoc_t auth_assoc_id;
+};
+
+enum { SCTP_AUTH_NEWKEY = 0, };
+
+
/*
* Described in Section 7.3
* Ancillary Data and Notification Interest Options
struct sctp_shutdown_event sn_shutdown_event;
struct sctp_adaptation_event sn_adaptation_event;
struct sctp_pdapi_event sn_pdapi_event;
+ struct sctp_authkey_event sn_authkey_event;
};
/* Section 5.3.1
SCTP_SHUTDOWN_EVENT,
SCTP_PARTIAL_DELIVERY_EVENT,
SCTP_ADAPTATION_INDICATION,
+ SCTP_AUTHENTICATION_EVENT,
};
/* Notification error codes used to fill up the error fields in some
__u32 spp_flags;
} __attribute__((packed, aligned(4)));
+/*
+ * 7.1.18. Add a chunk that must be authenticated (SCTP_AUTH_CHUNK)
+ *
+ * This set option adds a chunk type that the user is requesting to be
+ * received only in an authenticated way. Changes to the list of chunks
+ * will only effect future associations on the socket.
+ */
+struct sctp_authchunk {
+ __u8 sauth_chunk;
+};
+
+/*
+ * 7.1.19. Get or set the list of supported HMAC Identifiers (SCTP_HMAC_IDENT)
+ *
+ * This option gets or sets the list of HMAC algorithms that the local
+ * endpoint requires the peer to use.
+*/
+struct sctp_hmacalgo {
+ __u16 shmac_num_idents;
+ __u16 shmac_idents[];
+};
+
+/*
+ * 7.1.20. Set a shared key (SCTP_AUTH_KEY)
+ *
+ * This option will set a shared secret key which is used to build an
+ * association shared key.
+ */
+struct sctp_authkey {
+ sctp_assoc_t sca_assoc_id;
+ __u16 sca_keynumber;
+ __u16 sca_keylen;
+ __u8 sca_key[];
+};
+
+/*
+ * 7.1.21. Get or set the active shared key (SCTP_AUTH_ACTIVE_KEY)
+ *
+ * This option will get or set the active shared key to be used to build
+ * the association shared key.
+ */
+
+struct sctp_authkeyid {
+ sctp_assoc_t scact_assoc_id;
+ __u16 scact_keynumber;
+};
+
+
/* 7.1.23. Delayed Ack Timer (SCTP_DELAYED_ACK_TIME)
*
* This options will get or set the delayed ack timer. The time is set
struct sctp_paddrinfo sstat_primary;
};
+/*
+ * 7.2.3. Get the list of chunks the peer requires to be authenticated
+ * (SCTP_PEER_AUTH_CHUNKS)
+ *
+ * This option gets a list of chunks for a specified association that
+ * the peer requires to be received authenticated only.
+ */
+struct sctp_authchunks {
+ sctp_assoc_t gauth_assoc_id;
+ uint8_t gauth_chunks[];
+};
+
/*
* 8.3, 8.5 get all peer/local addresses in an association.
* This parameter struct is used by SCTP_GET_PEER_ADDRS and
if (free_key)
sctp_auth_key_put(asoc_key);
}
+
+/* API Helpers */
+
+/* Add a chunk to the endpoint authenticated chunk list */
+int sctp_auth_ep_add_chunkid(struct sctp_endpoint *ep, __u8 chunk_id)
+{
+ struct sctp_chunks_param *p = ep->auth_chunk_list;
+ __u16 nchunks;
+ __u16 param_len;
+
+ /* If this chunk is already specified, we are done */
+ if (__sctp_auth_cid(chunk_id, p))
+ return 0;
+
+ /* Check if we can add this chunk to the array */
+ param_len = ntohs(p->param_hdr.length);
+ nchunks = param_len - sizeof(sctp_paramhdr_t);
+ if (nchunks == SCTP_NUM_CHUNK_TYPES)
+ return -EINVAL;
+
+ p->chunks[nchunks] = chunk_id;
+ p->param_hdr.length = htons(param_len + 1);
+ return 0;
+}
+
+/* Add hmac identifires to the endpoint list of supported hmac ids */
+int sctp_auth_ep_set_hmacs(struct sctp_endpoint *ep,
+ struct sctp_hmacalgo *hmacs)
+{
+ int has_sha1 = 0;
+ __u16 id;
+ int i;
+
+ /* Scan the list looking for unsupported id. Also make sure that
+ * SHA1 is specified.
+ */
+ for (i = 0; i < hmacs->shmac_num_idents; i++) {
+ id = hmacs->shmac_idents[i];
+
+ if (SCTP_AUTH_HMAC_ID_SHA1 == id)
+ has_sha1 = 1;
+
+ if (!sctp_hmac_list[id].hmac_name)
+ return -EOPNOTSUPP;
+ }
+
+ if (!has_sha1)
+ return -EINVAL;
+
+ memcpy(ep->auth_hmacs_list->hmac_ids, &hmacs->shmac_idents[0],
+ hmacs->shmac_num_idents * sizeof(__u16));
+ ep->auth_hmacs_list->param_hdr.length = htons(sizeof(sctp_paramhdr_t) +
+ hmacs->shmac_num_idents * sizeof(__u16));
+ return 0;
+}
+
+/* Set a new shared key on either endpoint or association. If the
+ * the key with a same ID already exists, replace the key (remove the
+ * old key and add a new one).
+ */
+int sctp_auth_set_key(struct sctp_endpoint *ep,
+ struct sctp_association *asoc,
+ struct sctp_authkey *auth_key)
+{
+ struct sctp_shared_key *cur_key = NULL;
+ struct sctp_auth_bytes *key;
+ struct list_head *sh_keys;
+ int replace = 0;
+
+ /* Try to find the given key id to see if
+ * we are doing a replace, or adding a new key
+ */
+ if (asoc)
+ sh_keys = &asoc->endpoint_shared_keys;
+ else
+ sh_keys = &ep->endpoint_shared_keys;
+
+ key_for_each(cur_key, sh_keys) {
+ if (cur_key->key_id == auth_key->sca_keynumber) {
+ replace = 1;
+ break;
+ }
+ }
+
+ /* If we are not replacing a key id, we need to allocate
+ * a shared key.
+ */
+ if (!replace) {
+ cur_key = sctp_auth_shkey_create(auth_key->sca_keynumber,
+ GFP_KERNEL);
+ if (!cur_key)
+ return -ENOMEM;
+ }
+
+ /* Create a new key data based on the info passed in */
+ key = sctp_auth_create_key(auth_key->sca_keylen, GFP_KERNEL);
+ if (!key)
+ goto nomem;
+
+ memcpy(key->data, &auth_key->sca_key[0], auth_key->sca_keylen);
+
+ /* If we are replacing, remove the old keys data from the
+ * key id. If we are adding new key id, add it to the
+ * list.
+ */
+ if (replace)
+ sctp_auth_key_put(cur_key->key);
+ else
+ list_add(&cur_key->key_list, sh_keys);
+
+ cur_key->key = key;
+ sctp_auth_key_hold(key);
+
+ return 0;
+nomem:
+ if (!replace)
+ sctp_auth_shkey_free(cur_key);
+
+ return -ENOMEM;
+}
+
+int sctp_auth_set_active_key(struct sctp_endpoint *ep,
+ struct sctp_association *asoc,
+ __u16 key_id)
+{
+ struct sctp_shared_key *key;
+ struct list_head *sh_keys;
+ int found = 0;
+
+ /* The key identifier MUST correst to an existing key */
+ if (asoc)
+ sh_keys = &asoc->endpoint_shared_keys;
+ else
+ sh_keys = &ep->endpoint_shared_keys;
+
+ key_for_each(key, sh_keys) {
+ if (key->key_id == key_id) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found)
+ return -EINVAL;
+
+ if (asoc) {
+ asoc->active_key_id = key_id;
+ sctp_auth_asoc_init_active_key(asoc, GFP_KERNEL);
+ } else
+ ep->active_key_id = key_id;
+
+ return 0;
+}
+
+int sctp_auth_del_key_id(struct sctp_endpoint *ep,
+ struct sctp_association *asoc,
+ __u16 key_id)
+{
+ struct sctp_shared_key *key;
+ struct list_head *sh_keys;
+ int found = 0;
+
+ /* The key identifier MUST NOT be the current active key
+ * The key identifier MUST correst to an existing key
+ */
+ if (asoc) {
+ if (asoc->active_key_id == key_id)
+ return -EINVAL;
+
+ sh_keys = &asoc->endpoint_shared_keys;
+ } else {
+ if (ep->active_key_id == key_id)
+ return -EINVAL;
+
+ sh_keys = &ep->endpoint_shared_keys;
+ }
+
+ key_for_each(key, sh_keys) {
+ if (key->key_id == key_id) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found)
+ return -EINVAL;
+
+ /* Delete the shared key */
+ list_del_init(&key->key_list);
+ sctp_auth_shkey_free(key);
+
+ return 0;
+}
break;
}
+ if (asoc->active_key_id != ntohs(auth_hdr->shkey_id)) {
+ struct sctp_ulpevent *ev;
+
+ ev = sctp_ulpevent_make_authkey(asoc, ntohs(auth_hdr->shkey_id),
+ SCTP_AUTH_NEWKEY, GFP_ATOMIC);
+
+ if (!ev)
+ return -ENOMEM;
+
+ sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP,
+ SCTP_ULPEVENT(ev));
+ }
+
return SCTP_DISPOSITION_CONSUME;
}
return 0;
}
+/*
+ * 7.1.18. Add a chunk that must be authenticated (SCTP_AUTH_CHUNK)
+ *
+ * This set option adds a chunk type that the user is requesting to be
+ * received only in an authenticated way. Changes to the list of chunks
+ * will only effect future associations on the socket.
+ */
+static int sctp_setsockopt_auth_chunk(struct sock *sk,
+ char __user *optval,
+ int optlen)
+{
+ struct sctp_authchunk val;
+
+ if (optlen != sizeof(struct sctp_authchunk))
+ return -EINVAL;
+ if (copy_from_user(&val, optval, optlen))
+ return -EFAULT;
+
+ switch (val.sauth_chunk) {
+ case SCTP_CID_INIT:
+ case SCTP_CID_INIT_ACK:
+ case SCTP_CID_SHUTDOWN_COMPLETE:
+ case SCTP_CID_AUTH:
+ return -EINVAL;
+ }
+
+ /* add this chunk id to the endpoint */
+ return sctp_auth_ep_add_chunkid(sctp_sk(sk)->ep, val.sauth_chunk);
+}
+
+/*
+ * 7.1.19. Get or set the list of supported HMAC Identifiers (SCTP_HMAC_IDENT)
+ *
+ * This option gets or sets the list of HMAC algorithms that the local
+ * endpoint requires the peer to use.
+ */
+static int sctp_setsockopt_hmac_ident(struct sock *sk,
+ char __user *optval,
+ int optlen)
+{
+ struct sctp_hmacalgo *hmacs;
+ int err;
+
+ if (optlen < sizeof(struct sctp_hmacalgo))
+ return -EINVAL;
+
+ hmacs = kmalloc(optlen, GFP_KERNEL);
+ if (!hmacs)
+ return -ENOMEM;
+
+ if (copy_from_user(hmacs, optval, optlen)) {
+ err = -EFAULT;
+ goto out;
+ }
+
+ if (hmacs->shmac_num_idents == 0 ||
+ hmacs->shmac_num_idents > SCTP_AUTH_NUM_HMACS) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ err = sctp_auth_ep_set_hmacs(sctp_sk(sk)->ep, hmacs);
+out:
+ kfree(hmacs);
+ return err;
+}
+
+/*
+ * 7.1.20. Set a shared key (SCTP_AUTH_KEY)
+ *
+ * This option will set a shared secret key which is used to build an
+ * association shared key.
+ */
+static int sctp_setsockopt_auth_key(struct sock *sk,
+ char __user *optval,
+ int optlen)
+{
+ struct sctp_authkey *authkey;
+ struct sctp_association *asoc;
+ int ret;
+
+ if (optlen <= sizeof(struct sctp_authkey))
+ return -EINVAL;
+
+ authkey = kmalloc(optlen, GFP_KERNEL);
+ if (!authkey)
+ return -ENOMEM;
+
+ if (copy_from_user(authkey, optval, optlen)) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ asoc = sctp_id2assoc(sk, authkey->sca_assoc_id);
+ if (!asoc && authkey->sca_assoc_id && sctp_style(sk, UDP)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = sctp_auth_set_key(sctp_sk(sk)->ep, asoc, authkey);
+out:
+ kfree(authkey);
+ return ret;
+}
+
+/*
+ * 7.1.21. Get or set the active shared key (SCTP_AUTH_ACTIVE_KEY)
+ *
+ * This option will get or set the active shared key to be used to build
+ * the association shared key.
+ */
+static int sctp_setsockopt_active_key(struct sock *sk,
+ char __user *optval,
+ int optlen)
+{
+ struct sctp_authkeyid val;
+ struct sctp_association *asoc;
+
+ if (optlen != sizeof(struct sctp_authkeyid))
+ return -EINVAL;
+ if (copy_from_user(&val, optval, optlen))
+ return -EFAULT;
+
+ asoc = sctp_id2assoc(sk, val.scact_assoc_id);
+ if (!asoc && val.scact_assoc_id && sctp_style(sk, UDP))
+ return -EINVAL;
+
+ return sctp_auth_set_active_key(sctp_sk(sk)->ep, asoc,
+ val.scact_keynumber);
+}
+
+/*
+ * 7.1.22. Delete a shared key (SCTP_AUTH_DELETE_KEY)
+ *
+ * This set option will delete a shared secret key from use.
+ */
+static int sctp_setsockopt_del_key(struct sock *sk,
+ char __user *optval,
+ int optlen)
+{
+ struct sctp_authkeyid val;
+ struct sctp_association *asoc;
+
+ if (optlen != sizeof(struct sctp_authkeyid))
+ return -EINVAL;
+ if (copy_from_user(&val, optval, optlen))
+ return -EFAULT;
+
+ asoc = sctp_id2assoc(sk, val.scact_assoc_id);
+ if (!asoc && val.scact_assoc_id && sctp_style(sk, UDP))
+ return -EINVAL;
+
+ return sctp_auth_del_key_id(sctp_sk(sk)->ep, asoc,
+ val.scact_keynumber);
+
+}
+
+
/* API 6.2 setsockopt(), getsockopt()
*
* Applications use setsockopt() and getsockopt() to set or retrieve
case SCTP_MAX_BURST:
retval = sctp_setsockopt_maxburst(sk, optval, optlen);
break;
+ case SCTP_AUTH_CHUNK:
+ retval = sctp_setsockopt_auth_chunk(sk, optval, optlen);
+ break;
+ case SCTP_HMAC_IDENT:
+ retval = sctp_setsockopt_hmac_ident(sk, optval, optlen);
+ break;
+ case SCTP_AUTH_KEY:
+ retval = sctp_setsockopt_auth_key(sk, optval, optlen);
+ break;
+ case SCTP_AUTH_ACTIVE_KEY:
+ retval = sctp_setsockopt_active_key(sk, optval, optlen);
+ break;
+ case SCTP_AUTH_DELETE_KEY:
+ retval = sctp_setsockopt_del_key(sk, optval, optlen);
+ break;
default:
retval = -ENOPROTOOPT;
break;
return -ENOTSUPP;
}
+static int sctp_getsockopt_hmac_ident(struct sock *sk, int len,
+ char __user *optval, int __user *optlen)
+{
+ struct sctp_hmac_algo_param *hmacs;
+ __u16 param_len;
+
+ hmacs = sctp_sk(sk)->ep->auth_hmacs_list;
+ param_len = ntohs(hmacs->param_hdr.length);
+
+ if (len < param_len)
+ return -EINVAL;
+ if (put_user(len, optlen))
+ return -EFAULT;
+ if (copy_to_user(optval, hmacs->hmac_ids, len))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int sctp_getsockopt_active_key(struct sock *sk, int len,
+ char __user *optval, int __user *optlen)
+{
+ struct sctp_authkeyid val;
+ struct sctp_association *asoc;
+
+ if (len < sizeof(struct sctp_authkeyid))
+ return -EINVAL;
+ if (copy_from_user(&val, optval, sizeof(struct sctp_authkeyid)))
+ return -EFAULT;
+
+ asoc = sctp_id2assoc(sk, val.scact_assoc_id);
+ if (!asoc && val.scact_assoc_id && sctp_style(sk, UDP))
+ return -EINVAL;
+
+ if (asoc)
+ val.scact_keynumber = asoc->active_key_id;
+ else
+ val.scact_keynumber = sctp_sk(sk)->ep->active_key_id;
+
+ return 0;
+}
+
+static int sctp_getsockopt_peer_auth_chunks(struct sock *sk, int len,
+ char __user *optval, int __user *optlen)
+{
+ struct sctp_authchunks val;
+ struct sctp_association *asoc;
+ struct sctp_chunks_param *ch;
+ char __user *to;
+
+ if (len <= sizeof(struct sctp_authchunks))
+ return -EINVAL;
+
+ if (copy_from_user(&val, optval, sizeof(struct sctp_authchunks)))
+ return -EFAULT;
+
+ to = val.gauth_chunks;
+ asoc = sctp_id2assoc(sk, val.gauth_assoc_id);
+ if (!asoc)
+ return -EINVAL;
+
+ ch = asoc->peer.peer_chunks;
+
+ /* See if the user provided enough room for all the data */
+ if (len < ntohs(ch->param_hdr.length))
+ return -EINVAL;
+
+ len = ntohs(ch->param_hdr.length);
+ if (put_user(len, optlen))
+ return -EFAULT;
+ if (copy_to_user(to, ch->chunks, len))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int sctp_getsockopt_local_auth_chunks(struct sock *sk, int len,
+ char __user *optval, int __user *optlen)
+{
+ struct sctp_authchunks val;
+ struct sctp_association *asoc;
+ struct sctp_chunks_param *ch;
+ char __user *to;
+
+ if (len <= sizeof(struct sctp_authchunks))
+ return -EINVAL;
+
+ if (copy_from_user(&val, optval, sizeof(struct sctp_authchunks)))
+ return -EFAULT;
+
+ to = val.gauth_chunks;
+ asoc = sctp_id2assoc(sk, val.gauth_assoc_id);
+ if (!asoc && val.gauth_assoc_id && sctp_style(sk, UDP))
+ return -EINVAL;
+
+ if (asoc)
+ ch = (struct sctp_chunks_param*)asoc->c.auth_chunks;
+ else
+ ch = sctp_sk(sk)->ep->auth_chunk_list;
+
+ if (len < ntohs(ch->param_hdr.length))
+ return -EINVAL;
+
+ len = ntohs(ch->param_hdr.length);
+ if (put_user(len, optlen))
+ return -EFAULT;
+ if (copy_to_user(to, ch->chunks, len))
+ return -EFAULT;
+
+ return 0;
+}
+
SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
char __user *optval, int __user *optlen)
{
case SCTP_MAX_BURST:
retval = sctp_getsockopt_maxburst(sk, len, optval, optlen);
break;
+ case SCTP_AUTH_KEY:
+ case SCTP_AUTH_CHUNK:
+ case SCTP_AUTH_DELETE_KEY:
+ retval = -EOPNOTSUPP;
+ break;
+ case SCTP_HMAC_IDENT:
+ retval = sctp_getsockopt_hmac_ident(sk, len, optval, optlen);
+ break;
+ case SCTP_AUTH_ACTIVE_KEY:
+ retval = sctp_getsockopt_active_key(sk, len, optval, optlen);
+ break;
+ case SCTP_PEER_AUTH_CHUNKS:
+ retval = sctp_getsockopt_peer_auth_chunks(sk, len, optval,
+ optlen);
+ break;
+ case SCTP_LOCAL_AUTH_CHUNKS:
+ retval = sctp_getsockopt_local_auth_chunks(sk, len, optval,
+ optlen);
+ break;
default:
retval = -ENOPROTOOPT;
break;
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_EVENT;
+ 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.
*/