]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
Merge branch 'sctp-next'
authorDavid S. Miller <davem@davemloft.net>
Wed, 16 Jul 2014 21:41:10 +0000 (14:41 -0700)
committerDavid S. Miller <davem@davemloft.net>
Wed, 16 Jul 2014 21:41:10 +0000 (14:41 -0700)
Daniel Borkmann says:

====================
SCTP updates

This set improves the SCTP socket API to be more in line with RFC6458,
Geir and myself have finalized it eventually. While at it, the first
patch also fixes two possible information leaks that should go to net
tree as well (therefore the change is already here in net-next via a
merge of the 'net' tree -DaveM). For more details, I refer you to the
patches themselves.

Thanks a lot.

v1 -> v2:
 - Added 6th patch to deprecate SCTP_SNDRCV, rest unchanged
====================

CC: Jay Vosburgh <j.vosburgh@gmail.com>
CC: Andy Gospodarek <andy@greyhouse.net>
Signed-off-by: Veaceslav Falico <vfalico@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/sctp/sctp.h
include/net/sctp/structs.h
include/net/sctp/ulpevent.h
include/uapi/linux/sctp.h
net/sctp/socket.c
net/sctp/ulpevent.c

index c2035c96a2ee150a833ab28aa71e510abab72a5b..90c1cccd164d11bb8a42fc03e313d711cc67e882 100644 (file)
@@ -109,6 +109,7 @@ void sctp_copy_sock(struct sock *newsk, struct sock *sk,
                    struct sctp_association *asoc);
 extern struct percpu_counter sctp_sockets_allocated;
 int sctp_asconf_mgmt(struct sctp_sock *, struct sctp_sockaddr_entry *);
+struct sk_buff *sctp_skb_recv_datagram(struct sock *, int, int, int *);
 
 /*
  * sctp/primitive.c
index f38588bf3462d9e2374258bb6c383e38413507ed..7741d1b66967a3f198bb33017d22103454de5a94 100644 (file)
@@ -207,7 +207,9 @@ struct sctp_sock {
        struct sctp_paddrparams paddrparam;
        struct sctp_event_subscribe subscribe;
        struct sctp_assocparams assocparams;
+
        int user_frag;
+
        __u32 autoclose;
        __u8 nodelay;
        __u8 disable_fragments;
@@ -215,6 +217,8 @@ struct sctp_sock {
        __u8 frag_interleave;
        __u32 adaptation_ind;
        __u32 pd_point;
+       __u8 recvrcvinfo;
+       __u8 recvnxtinfo;
 
        atomic_t pd_mode;
        /* Receive to here while partial delivery is in effect. */
@@ -1919,7 +1923,8 @@ struct sctp_chunk *sctp_get_ecne_prepend(struct sctp_association *asoc);
 /* A convenience structure to parse out SCTP specific CMSGs. */
 typedef struct sctp_cmsgs {
        struct sctp_initmsg *init;
-       struct sctp_sndrcvinfo *info;
+       struct sctp_sndrcvinfo *srinfo;
+       struct sctp_sndinfo *sinfo;
 } sctp_cmsgs_t;
 
 /* Structure for tracking memory objects */
index daacb32b55b576359443cb57d184a8c6639d4956..cccdcfd149736b315554d64c2a556e0ad6496fc8 100644 (file)
@@ -129,7 +129,12 @@ struct sctp_ulpevent *sctp_ulpevent_make_sender_dry_event(
        const struct sctp_association *asoc, gfp_t gfp);
 
 void sctp_ulpevent_read_sndrcvinfo(const struct sctp_ulpevent *event,
-       struct msghdr *);
+                                  struct msghdr *);
+void sctp_ulpevent_read_rcvinfo(const struct sctp_ulpevent *event,
+                               struct msghdr *);
+void sctp_ulpevent_read_nxtinfo(const struct sctp_ulpevent *event,
+                               struct msghdr *, struct sock *sk);
+
 __u16 sctp_ulpevent_get_notification_type(const struct sctp_ulpevent *event);
 
 /* Is this event type enabled? */
@@ -155,10 +160,3 @@ static inline int sctp_ulpevent_is_enabled(const struct sctp_ulpevent *event,
 }
 
 #endif /* __sctp_ulpevent_h__ */
-
-
-
-
-
-
-
index 266022a2be4acae990ed56c3efe783766d667d6e..ce70fe6b45df3e841c35accbdb6379c16563893c 100644 (file)
@@ -95,6 +95,9 @@ typedef __s32 sctp_assoc_t;
 #define SCTP_GET_ASSOC_ID_LIST 29      /* Read only */
 #define SCTP_AUTO_ASCONF       30
 #define SCTP_PEER_ADDR_THLDS   31
+#define SCTP_RECVRCVINFO       32
+#define SCTP_RECVNXTINFO       33
+#define SCTP_DEFAULT_SNDINFO   34
 
 /* Internal Socket Options. Some of the sctp library functions are
  * implemented using these socket options.
@@ -110,8 +113,14 @@ typedef __s32 sctp_assoc_t;
 #define SCTP_SOCKOPT_CONNECTX3 111     /* CONNECTX requests (updated) */
 #define SCTP_GET_ASSOC_STATS   112     /* Read only */
 
-/*
- * 5.2.1 SCTP Initiation Structure (SCTP_INIT)
+/* These are bit fields for msghdr->msg_flags.  See section 5.1.  */
+/* On user space Linux, these live in <bits/socket.h> as an enum.  */
+enum sctp_msg_flags {
+       MSG_NOTIFICATION = 0x8000,
+#define MSG_NOTIFICATION MSG_NOTIFICATION
+};
+
+/* 5.3.1 SCTP Initiation Structure (SCTP_INIT)
  *
  *   This cmsghdr structure provides information for initializing new
  *   SCTP associations with sendmsg().  The SCTP_INITMSG socket option
@@ -121,7 +130,6 @@ typedef __s32 sctp_assoc_t;
  *   cmsg_level    cmsg_type      cmsg_data[]
  *   ------------  ------------   ----------------------
  *   IPPROTO_SCTP  SCTP_INIT      struct sctp_initmsg
- *
  */
 struct sctp_initmsg {
        __u16 sinit_num_ostreams;
@@ -130,8 +138,7 @@ struct sctp_initmsg {
        __u16 sinit_max_init_timeo;
 };
 
-/*
- * 5.2.2 SCTP Header Information Structure (SCTP_SNDRCV)
+/* 5.3.2 SCTP Header Information Structure (SCTP_SNDRCV)
  *
  *   This cmsghdr structure specifies SCTP options for sendmsg() and
  *   describes SCTP header information about a received message through
@@ -140,7 +147,6 @@ struct sctp_initmsg {
  *   cmsg_level    cmsg_type      cmsg_data[]
  *   ------------  ------------   ----------------------
  *   IPPROTO_SCTP  SCTP_SNDRCV    struct sctp_sndrcvinfo
- *
  */
 struct sctp_sndrcvinfo {
        __u16 sinfo_stream;
@@ -154,19 +160,74 @@ struct sctp_sndrcvinfo {
        sctp_assoc_t sinfo_assoc_id;
 };
 
+/* 5.3.4 SCTP Send Information Structure (SCTP_SNDINFO)
+ *
+ *   This cmsghdr structure specifies SCTP options for sendmsg().
+ *
+ *   cmsg_level    cmsg_type      cmsg_data[]
+ *   ------------  ------------   -------------------
+ *   IPPROTO_SCTP  SCTP_SNDINFO   struct sctp_sndinfo
+ */
+struct sctp_sndinfo {
+       __u16 snd_sid;
+       __u16 snd_flags;
+       __u32 snd_ppid;
+       __u32 snd_context;
+       sctp_assoc_t snd_assoc_id;
+};
+
+/* 5.3.5 SCTP Receive Information Structure (SCTP_RCVINFO)
+ *
+ *   This cmsghdr structure describes SCTP receive information
+ *   about a received message through recvmsg().
+ *
+ *   cmsg_level    cmsg_type      cmsg_data[]
+ *   ------------  ------------   -------------------
+ *   IPPROTO_SCTP  SCTP_RCVINFO   struct sctp_rcvinfo
+ */
+struct sctp_rcvinfo {
+       __u16 rcv_sid;
+       __u16 rcv_ssn;
+       __u16 rcv_flags;
+       __u32 rcv_ppid;
+       __u32 rcv_tsn;
+       __u32 rcv_cumtsn;
+       __u32 rcv_context;
+       sctp_assoc_t rcv_assoc_id;
+};
+
+/* 5.3.6 SCTP Next Receive Information Structure (SCTP_NXTINFO)
+ *
+ *   This cmsghdr structure describes SCTP receive information
+ *   of the next message that will be delivered through recvmsg()
+ *   if this information is already available when delivering
+ *   the current message.
+ *
+ *   cmsg_level    cmsg_type      cmsg_data[]
+ *   ------------  ------------   -------------------
+ *   IPPROTO_SCTP  SCTP_NXTINFO   struct sctp_nxtinfo
+ */
+struct sctp_nxtinfo {
+       __u16 nxt_sid;
+       __u16 nxt_flags;
+       __u32 nxt_ppid;
+       __u32 nxt_length;
+       sctp_assoc_t nxt_assoc_id;
+};
+
 /*
  *  sinfo_flags: 16 bits (unsigned integer)
  *
  *   This field may contain any of the following flags and is composed of
  *   a bitwise OR of these values.
  */
-
 enum sctp_sinfo_flags {
-       SCTP_UNORDERED = 1,  /* Send/receive message unordered. */
-       SCTP_ADDR_OVER = 2,  /* Override the primary destination. */
-       SCTP_ABORT=4,        /* Send an ABORT message to the peer. */
-       SCTP_SACK_IMMEDIATELY = 8,      /* SACK should be sent without delay */
-       SCTP_EOF=MSG_FIN,    /* Initiate graceful shutdown process. */
+       SCTP_UNORDERED          = (1 << 0), /* Send/receive message unordered. */
+       SCTP_ADDR_OVER          = (1 << 1), /* Override the primary destination. */
+       SCTP_ABORT              = (1 << 2), /* Send an ABORT message to the peer. */
+       SCTP_SACK_IMMEDIATELY   = (1 << 3), /* SACK should be sent without delay. */
+       SCTP_NOTIFICATION       = MSG_NOTIFICATION, /* Next message is not user msg but notification. */
+       SCTP_EOF                = MSG_FIN,  /* Initiate graceful shutdown process. */
 };
 
 typedef union {
@@ -177,10 +238,16 @@ typedef union {
 
 /* These are cmsg_types.  */
 typedef enum sctp_cmsg_type {
-       SCTP_INIT,              /* 5.2.1 SCTP Initiation Structure */
+       SCTP_INIT,              /* 5.2.1 SCTP Initiation Structure */
 #define SCTP_INIT      SCTP_INIT
-       SCTP_SNDRCV,            /* 5.2.2 SCTP Header Information Structure */
+       SCTP_SNDRCV,            /* 5.2.2 SCTP Header Information Structure */
 #define SCTP_SNDRCV    SCTP_SNDRCV
+       SCTP_SNDINFO,           /* 5.3.4 SCTP Send Information Structure */
+#define SCTP_SNDINFO   SCTP_SNDINFO
+       SCTP_RCVINFO,           /* 5.3.5 SCTP Receive Information Structure */
+#define SCTP_RCVINFO   SCTP_RCVINFO
+       SCTP_NXTINFO,           /* 5.3.6 SCTP Next Receive Information Structure */
+#define SCTP_NXTINFO   SCTP_NXTINFO
 } sctp_cmsg_t;
 
 /*
@@ -808,13 +875,6 @@ struct sctp_assoc_stats {
        __u64           sas_ictrlchunks; /* Control chunks received */
 };
 
-/* These are bit fields for msghdr->msg_flags.  See section 5.1.  */
-/* On user space Linux, these live in <bits/socket.h> as an enum.  */
-enum sctp_msg_flags {
-       MSG_NOTIFICATION = 0x8000,
-#define MSG_NOTIFICATION MSG_NOTIFICATION
-};
-
 /*
  * 8.1 sctp_bindx()
  *
index 429899689408caec64cf5c36b4eb0b00369f8e48..743308f40544c743e3cd78d8e77dab55156d57a5 100644 (file)
@@ -1602,12 +1602,13 @@ static int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
        struct sctp_initmsg *sinit;
        sctp_assoc_t associd = 0;
        sctp_cmsgs_t cmsgs = { NULL };
-       int err;
        sctp_scope_t scope;
-       long timeo;
-       __u16 sinfo_flags = 0;
+       bool fill_sinfo_ttl = false;
        struct sctp_datamsg *datamsg;
        int msg_flags = msg->msg_flags;
+       __u16 sinfo_flags = 0;
+       long timeo;
+       int err;
 
        err = 0;
        sp = sctp_sk(sk);
@@ -1648,10 +1649,21 @@ static int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
                msg_name = msg->msg_name;
        }
 
-       sinfo = cmsgs.info;
        sinit = cmsgs.init;
+       if (cmsgs.sinfo != NULL) {
+               memset(&default_sinfo, 0, sizeof(default_sinfo));
+               default_sinfo.sinfo_stream = cmsgs.sinfo->snd_sid;
+               default_sinfo.sinfo_flags = cmsgs.sinfo->snd_flags;
+               default_sinfo.sinfo_ppid = cmsgs.sinfo->snd_ppid;
+               default_sinfo.sinfo_context = cmsgs.sinfo->snd_context;
+               default_sinfo.sinfo_assoc_id = cmsgs.sinfo->snd_assoc_id;
 
-       /* Did the user specify SNDRCVINFO?  */
+               sinfo = &default_sinfo;
+               fill_sinfo_ttl = true;
+       } else {
+               sinfo = cmsgs.srinfo;
+       }
+       /* Did the user specify SNDINFO/SNDRCVINFO? */
        if (sinfo) {
                sinfo_flags = sinfo->sinfo_flags;
                associd = sinfo->sinfo_assoc_id;
@@ -1858,8 +1870,8 @@ static int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
        pr_debug("%s: we have a valid association\n", __func__);
 
        if (!sinfo) {
-               /* If the user didn't specify SNDRCVINFO, make up one with
-                * some defaults.
+               /* If the user didn't specify SNDINFO/SNDRCVINFO, make up
+                * one with some defaults.
                 */
                memset(&default_sinfo, 0, sizeof(default_sinfo));
                default_sinfo.sinfo_stream = asoc->default_stream;
@@ -1868,7 +1880,13 @@ static int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
                default_sinfo.sinfo_context = asoc->default_context;
                default_sinfo.sinfo_timetolive = asoc->default_timetolive;
                default_sinfo.sinfo_assoc_id = sctp_assoc2id(asoc);
+
                sinfo = &default_sinfo;
+       } else if (fill_sinfo_ttl) {
+               /* In case SNDINFO was specified, we still need to fill
+                * it with a default ttl from the assoc here.
+                */
+               sinfo->sinfo_timetolive = asoc->default_timetolive;
        }
 
        /* API 7.1.7, the sndbuf size per association bounds the
@@ -2042,8 +2060,6 @@ static int sctp_skb_pull(struct sk_buff *skb, int len)
  *  flags   - flags sent or received with the user message, see Section
  *            5 for complete description of the flags.
  */
-static struct sk_buff *sctp_skb_recv_datagram(struct sock *, int, int, int *);
-
 static int sctp_recvmsg(struct kiocb *iocb, struct sock *sk,
                        struct msghdr *msg, size_t len, int noblock,
                        int flags, int *addr_len)
@@ -2094,9 +2110,16 @@ static int sctp_recvmsg(struct kiocb *iocb, struct sock *sk,
                sp->pf->skb_msgname(skb, msg->msg_name, addr_len);
        }
 
+       /* Check if we allow SCTP_NXTINFO. */
+       if (sp->recvnxtinfo)
+               sctp_ulpevent_read_nxtinfo(event, msg, sk);
+       /* Check if we allow SCTP_RCVINFO. */
+       if (sp->recvrcvinfo)
+               sctp_ulpevent_read_rcvinfo(event, msg);
        /* Check if we allow SCTP_SNDRCVINFO. */
        if (sp->subscribe.sctp_data_io_event)
                sctp_ulpevent_read_sndrcvinfo(event, msg);
+
 #if 0
        /* FIXME: we should be calling IP/IPv6 layers.  */
        if (sk->sk_protinfo.af_inet.cmsg_flags)
@@ -2182,8 +2205,13 @@ static int sctp_setsockopt_events(struct sock *sk, char __user *optval,
        if (copy_from_user(&sctp_sk(sk)->subscribe, optval, optlen))
                return -EFAULT;
 
-       /*
-        * At the time when a user app subscribes to SCTP_SENDER_DRY_EVENT,
+       if (sctp_sk(sk)->subscribe.sctp_data_io_event)
+               pr_warn_ratelimited(DEPRECATED "%s (pid %d) "
+                                   "Requested SCTP_SNDRCVINFO event.\n"
+                                   "Use SCTP_RCVINFO through SCTP_RECVRCVINFO option instead.\n",
+                                   current->comm, task_pid_nr(current));
+
+       /* At the time when a user app subscribes to SCTP_SENDER_DRY_EVENT,
         * if there is no data to be sent or retransmit, the stack will
         * immediately send up this notification.
         */
@@ -2747,19 +2775,22 @@ static int sctp_setsockopt_default_send_param(struct sock *sk,
                                              char __user *optval,
                                              unsigned int optlen)
 {
-       struct sctp_sndrcvinfo info;
-       struct sctp_association *asoc;
        struct sctp_sock *sp = sctp_sk(sk);
+       struct sctp_association *asoc;
+       struct sctp_sndrcvinfo info;
 
-       if (optlen != sizeof(struct sctp_sndrcvinfo))
+       if (optlen != sizeof(info))
                return -EINVAL;
        if (copy_from_user(&info, optval, optlen))
                return -EFAULT;
+       if (info.sinfo_flags &
+           ~(SCTP_UNORDERED | SCTP_ADDR_OVER |
+             SCTP_ABORT | SCTP_EOF))
+               return -EINVAL;
 
        asoc = sctp_id2assoc(sk, info.sinfo_assoc_id);
        if (!asoc && info.sinfo_assoc_id && sctp_style(sk, UDP))
                return -EINVAL;
-
        if (asoc) {
                asoc->default_stream = info.sinfo_stream;
                asoc->default_flags = info.sinfo_flags;
@@ -2777,6 +2808,44 @@ static int sctp_setsockopt_default_send_param(struct sock *sk,
        return 0;
 }
 
+/* RFC6458, Section 8.1.31. Set/get Default Send Parameters
+ * (SCTP_DEFAULT_SNDINFO)
+ */
+static int sctp_setsockopt_default_sndinfo(struct sock *sk,
+                                          char __user *optval,
+                                          unsigned int optlen)
+{
+       struct sctp_sock *sp = sctp_sk(sk);
+       struct sctp_association *asoc;
+       struct sctp_sndinfo info;
+
+       if (optlen != sizeof(info))
+               return -EINVAL;
+       if (copy_from_user(&info, optval, optlen))
+               return -EFAULT;
+       if (info.snd_flags &
+           ~(SCTP_UNORDERED | SCTP_ADDR_OVER |
+             SCTP_ABORT | SCTP_EOF))
+               return -EINVAL;
+
+       asoc = sctp_id2assoc(sk, info.snd_assoc_id);
+       if (!asoc && info.snd_assoc_id && sctp_style(sk, UDP))
+               return -EINVAL;
+       if (asoc) {
+               asoc->default_stream = info.snd_sid;
+               asoc->default_flags = info.snd_flags;
+               asoc->default_ppid = info.snd_ppid;
+               asoc->default_context = info.snd_context;
+       } else {
+               sp->default_stream = info.snd_sid;
+               sp->default_flags = info.snd_flags;
+               sp->default_ppid = info.snd_ppid;
+               sp->default_context = info.snd_context;
+       }
+
+       return 0;
+}
+
 /* 7.1.10 Set Primary Address (SCTP_PRIMARY_ADDR)
  *
  * Requests that the local SCTP stack use the enclosed peer address as
@@ -3523,7 +3592,6 @@ static int sctp_setsockopt_auto_asconf(struct sock *sk, char __user *optval,
        return 0;
 }
 
-
 /*
  * SCTP_PEER_ADDR_THLDS
  *
@@ -3574,6 +3642,38 @@ static int sctp_setsockopt_paddr_thresholds(struct sock *sk,
        return 0;
 }
 
+static int sctp_setsockopt_recvrcvinfo(struct sock *sk,
+                                      char __user *optval,
+                                      unsigned int optlen)
+{
+       int val;
+
+       if (optlen < sizeof(int))
+               return -EINVAL;
+       if (get_user(val, (int __user *) optval))
+               return -EFAULT;
+
+       sctp_sk(sk)->recvrcvinfo = (val == 0) ? 0 : 1;
+
+       return 0;
+}
+
+static int sctp_setsockopt_recvnxtinfo(struct sock *sk,
+                                      char __user *optval,
+                                      unsigned int optlen)
+{
+       int val;
+
+       if (optlen < sizeof(int))
+               return -EINVAL;
+       if (get_user(val, (int __user *) optval))
+               return -EFAULT;
+
+       sctp_sk(sk)->recvnxtinfo = (val == 0) ? 0 : 1;
+
+       return 0;
+}
+
 /* API 6.2 setsockopt(), getsockopt()
  *
  * Applications use setsockopt() and getsockopt() to set or retrieve
@@ -3671,6 +3771,9 @@ static int sctp_setsockopt(struct sock *sk, int level, int optname,
                retval = sctp_setsockopt_default_send_param(sk, optval,
                                                            optlen);
                break;
+       case SCTP_DEFAULT_SNDINFO:
+               retval = sctp_setsockopt_default_sndinfo(sk, optval, optlen);
+               break;
        case SCTP_PRIMARY_ADDR:
                retval = sctp_setsockopt_primary_addr(sk, optval, optlen);
                break;
@@ -3725,6 +3828,12 @@ static int sctp_setsockopt(struct sock *sk, int level, int optname,
        case SCTP_PEER_ADDR_THLDS:
                retval = sctp_setsockopt_paddr_thresholds(sk, optval, optlen);
                break;
+       case SCTP_RECVRCVINFO:
+               retval = sctp_setsockopt_recvrcvinfo(sk, optval, optlen);
+               break;
+       case SCTP_RECVNXTINFO:
+               retval = sctp_setsockopt_recvnxtinfo(sk, optval, optlen);
+               break;
        default:
                retval = -ENOPROTOOPT;
                break;
@@ -3971,6 +4080,9 @@ static int sctp_init_sock(struct sock *sk)
        /* Enable Nagle algorithm by default.  */
        sp->nodelay           = 0;
 
+       sp->recvrcvinfo = 0;
+       sp->recvnxtinfo = 0;
+
        /* Enable by default. */
        sp->v4mapped          = 1;
 
@@ -4964,14 +5076,14 @@ static int sctp_getsockopt_default_send_param(struct sock *sk,
                                        int len, char __user *optval,
                                        int __user *optlen)
 {
-       struct sctp_sndrcvinfo info;
-       struct sctp_association *asoc;
        struct sctp_sock *sp = sctp_sk(sk);
+       struct sctp_association *asoc;
+       struct sctp_sndrcvinfo info;
 
-       if (len < sizeof(struct sctp_sndrcvinfo))
+       if (len < sizeof(info))
                return -EINVAL;
 
-       len = sizeof(struct sctp_sndrcvinfo);
+       len = sizeof(info);
 
        if (copy_from_user(&info, optval, len))
                return -EFAULT;
@@ -4979,7 +5091,6 @@ static int sctp_getsockopt_default_send_param(struct sock *sk,
        asoc = sctp_id2assoc(sk, info.sinfo_assoc_id);
        if (!asoc && info.sinfo_assoc_id && sctp_style(sk, UDP))
                return -EINVAL;
-
        if (asoc) {
                info.sinfo_stream = asoc->default_stream;
                info.sinfo_flags = asoc->default_flags;
@@ -5002,6 +5113,48 @@ static int sctp_getsockopt_default_send_param(struct sock *sk,
        return 0;
 }
 
+/* RFC6458, Section 8.1.31. Set/get Default Send Parameters
+ * (SCTP_DEFAULT_SNDINFO)
+ */
+static int sctp_getsockopt_default_sndinfo(struct sock *sk, int len,
+                                          char __user *optval,
+                                          int __user *optlen)
+{
+       struct sctp_sock *sp = sctp_sk(sk);
+       struct sctp_association *asoc;
+       struct sctp_sndinfo info;
+
+       if (len < sizeof(info))
+               return -EINVAL;
+
+       len = sizeof(info);
+
+       if (copy_from_user(&info, optval, len))
+               return -EFAULT;
+
+       asoc = sctp_id2assoc(sk, info.snd_assoc_id);
+       if (!asoc && info.snd_assoc_id && sctp_style(sk, UDP))
+               return -EINVAL;
+       if (asoc) {
+               info.snd_sid = asoc->default_stream;
+               info.snd_flags = asoc->default_flags;
+               info.snd_ppid = asoc->default_ppid;
+               info.snd_context = asoc->default_context;
+       } else {
+               info.snd_sid = sp->default_stream;
+               info.snd_flags = sp->default_flags;
+               info.snd_ppid = sp->default_ppid;
+               info.snd_context = sp->default_context;
+       }
+
+       if (put_user(len, optlen))
+               return -EFAULT;
+       if (copy_to_user(optval, &info, len))
+               return -EFAULT;
+
+       return 0;
+}
+
 /*
  *
  * 7.1.5 SCTP_NODELAY
@@ -5752,6 +5905,46 @@ static int sctp_getsockopt_assoc_stats(struct sock *sk, int len,
        return 0;
 }
 
+static int sctp_getsockopt_recvrcvinfo(struct sock *sk,        int len,
+                                      char __user *optval,
+                                      int __user *optlen)
+{
+       int val = 0;
+
+       if (len < sizeof(int))
+               return -EINVAL;
+
+       len = sizeof(int);
+       if (sctp_sk(sk)->recvrcvinfo)
+               val = 1;
+       if (put_user(len, optlen))
+               return -EFAULT;
+       if (copy_to_user(optval, &val, len))
+               return -EFAULT;
+
+       return 0;
+}
+
+static int sctp_getsockopt_recvnxtinfo(struct sock *sk,        int len,
+                                      char __user *optval,
+                                      int __user *optlen)
+{
+       int val = 0;
+
+       if (len < sizeof(int))
+               return -EINVAL;
+
+       len = sizeof(int);
+       if (sctp_sk(sk)->recvnxtinfo)
+               val = 1;
+       if (put_user(len, optlen))
+               return -EFAULT;
+       if (copy_to_user(optval, &val, len))
+               return -EFAULT;
+
+       return 0;
+}
+
 static int sctp_getsockopt(struct sock *sk, int level, int optname,
                           char __user *optval, int __user *optlen)
 {
@@ -5821,6 +6014,10 @@ static int sctp_getsockopt(struct sock *sk, int level, int optname,
                retval = sctp_getsockopt_default_send_param(sk, len,
                                                            optval, optlen);
                break;
+       case SCTP_DEFAULT_SNDINFO:
+               retval = sctp_getsockopt_default_sndinfo(sk, len,
+                                                        optval, optlen);
+               break;
        case SCTP_PRIMARY_ADDR:
                retval = sctp_getsockopt_primary_addr(sk, len, optval, optlen);
                break;
@@ -5895,6 +6092,12 @@ static int sctp_getsockopt(struct sock *sk, int level, int optname,
        case SCTP_GET_ASSOC_STATS:
                retval = sctp_getsockopt_assoc_stats(sk, len, optval, optlen);
                break;
+       case SCTP_RECVRCVINFO:
+               retval = sctp_getsockopt_recvrcvinfo(sk, len, optval, optlen);
+               break;
+       case SCTP_RECVNXTINFO:
+               retval = sctp_getsockopt_recvnxtinfo(sk, len, optval, optlen);
+               break;
        default:
                retval = -ENOPROTOOPT;
                break;
@@ -6390,8 +6593,7 @@ static int sctp_msghdr_parse(const struct msghdr *msg, sctp_cmsgs_t *cmsgs)
        struct cmsghdr *cmsg;
        struct msghdr *my_msg = (struct msghdr *)msg;
 
-       for (cmsg = CMSG_FIRSTHDR(msg);
-            cmsg != NULL;
+       for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL;
             cmsg = CMSG_NXTHDR(my_msg, cmsg)) {
                if (!CMSG_OK(my_msg, cmsg))
                        return -EINVAL;
@@ -6404,7 +6606,7 @@ static int sctp_msghdr_parse(const struct msghdr *msg, sctp_cmsgs_t *cmsgs)
                switch (cmsg->cmsg_type) {
                case SCTP_INIT:
                        /* SCTP Socket API Extension
-                        * 5.2.1 SCTP Initiation Structure (SCTP_INIT)
+                        * 5.3.1 SCTP Initiation Structure (SCTP_INIT)
                         *
                         * This cmsghdr structure provides information for
                         * initializing new SCTP associations with sendmsg().
@@ -6416,15 +6618,15 @@ static int sctp_msghdr_parse(const struct msghdr *msg, sctp_cmsgs_t *cmsgs)
                         * ------------  ------------   ----------------------
                         * IPPROTO_SCTP  SCTP_INIT      struct sctp_initmsg
                         */
-                       if (cmsg->cmsg_len !=
-                           CMSG_LEN(sizeof(struct sctp_initmsg)))
+                       if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct sctp_initmsg)))
                                return -EINVAL;
-                       cmsgs->init = (struct sctp_initmsg *)CMSG_DATA(cmsg);
+
+                       cmsgs->init = CMSG_DATA(cmsg);
                        break;
 
                case SCTP_SNDRCV:
                        /* SCTP Socket API Extension
-                        * 5.2.2 SCTP Header Information Structure(SCTP_SNDRCV)
+                        * 5.3.2 SCTP Header Information Structure(SCTP_SNDRCV)
                         *
                         * This cmsghdr structure specifies SCTP options for
                         * sendmsg() and describes SCTP header information
@@ -6434,24 +6636,44 @@ static int sctp_msghdr_parse(const struct msghdr *msg, sctp_cmsgs_t *cmsgs)
                         * ------------  ------------   ----------------------
                         * IPPROTO_SCTP  SCTP_SNDRCV    struct sctp_sndrcvinfo
                         */
-                       if (cmsg->cmsg_len !=
-                           CMSG_LEN(sizeof(struct sctp_sndrcvinfo)))
+                       if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct sctp_sndrcvinfo)))
                                return -EINVAL;
 
-                       cmsgs->info =
-                               (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg);
+                       cmsgs->srinfo = CMSG_DATA(cmsg);
 
-                       /* Minimally, validate the sinfo_flags. */
-                       if (cmsgs->info->sinfo_flags &
+                       if (cmsgs->srinfo->sinfo_flags &
                            ~(SCTP_UNORDERED | SCTP_ADDR_OVER |
                              SCTP_ABORT | SCTP_EOF))
                                return -EINVAL;
                        break;
 
+               case SCTP_SNDINFO:
+                       /* SCTP Socket API Extension
+                        * 5.3.4 SCTP Send Information Structure (SCTP_SNDINFO)
+                        *
+                        * This cmsghdr structure specifies SCTP options for
+                        * sendmsg(). This structure and SCTP_RCVINFO replaces
+                        * SCTP_SNDRCV which has been deprecated.
+                        *
+                        * cmsg_level    cmsg_type      cmsg_data[]
+                        * ------------  ------------   ---------------------
+                        * IPPROTO_SCTP  SCTP_SNDINFO    struct sctp_sndinfo
+                        */
+                       if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct sctp_sndinfo)))
+                               return -EINVAL;
+
+                       cmsgs->sinfo = CMSG_DATA(cmsg);
+
+                       if (cmsgs->sinfo->snd_flags &
+                           ~(SCTP_UNORDERED | SCTP_ADDR_OVER |
+                             SCTP_ABORT | SCTP_EOF))
+                               return -EINVAL;
+                       break;
                default:
                        return -EINVAL;
                }
        }
+
        return 0;
 }
 
@@ -6518,8 +6740,8 @@ out:
  * Note: This is pretty much the same routine as in core/datagram.c
  * with a few changes to make lksctp work.
  */
-static struct sk_buff *sctp_skb_recv_datagram(struct sock *sk, int flags,
-                                             int noblock, int *err)
+struct sk_buff *sctp_skb_recv_datagram(struct sock *sk, int flags,
+                                      int noblock, int *err)
 {
        int error;
        struct sk_buff *skb;
index b6842fdb53d4b09ffdafec78c2bab535e6eaad2d..e049298ecfa0f2a981ad5366161c4484ac67303c 100644 (file)
@@ -886,6 +886,69 @@ void sctp_ulpevent_read_sndrcvinfo(const struct sctp_ulpevent *event,
                 sizeof(sinfo), &sinfo);
 }
 
+/* RFC6458, Section 5.3.5 SCTP Receive Information Structure
+ * (SCTP_SNDRCV)
+ */
+void sctp_ulpevent_read_rcvinfo(const struct sctp_ulpevent *event,
+                               struct msghdr *msghdr)
+{
+       struct sctp_rcvinfo rinfo;
+
+       if (sctp_ulpevent_is_notification(event))
+               return;
+
+       memset(&rinfo, 0, sizeof(struct sctp_rcvinfo));
+       rinfo.rcv_sid = event->stream;
+       rinfo.rcv_ssn = event->ssn;
+       rinfo.rcv_ppid = event->ppid;
+       rinfo.rcv_flags = event->flags;
+       rinfo.rcv_tsn = event->tsn;
+       rinfo.rcv_cumtsn = event->cumtsn;
+       rinfo.rcv_assoc_id = sctp_assoc2id(event->asoc);
+       rinfo.rcv_context = event->asoc->default_rcv_context;
+
+       put_cmsg(msghdr, IPPROTO_SCTP, SCTP_RCVINFO,
+                sizeof(rinfo), &rinfo);
+}
+
+/* RFC6458, Section 5.3.6. SCTP Next Receive Information Structure
+ * (SCTP_NXTINFO)
+ */
+static void __sctp_ulpevent_read_nxtinfo(const struct sctp_ulpevent *event,
+                                        struct msghdr *msghdr,
+                                        const struct sk_buff *skb)
+{
+       struct sctp_nxtinfo nxtinfo;
+
+       memset(&nxtinfo, 0, sizeof(nxtinfo));
+       nxtinfo.nxt_sid = event->stream;
+       nxtinfo.nxt_ppid = event->ppid;
+       nxtinfo.nxt_flags = event->flags;
+       if (sctp_ulpevent_is_notification(event))
+               nxtinfo.nxt_flags |= SCTP_NOTIFICATION;
+       nxtinfo.nxt_length = skb->len;
+       nxtinfo.nxt_assoc_id = sctp_assoc2id(event->asoc);
+
+       put_cmsg(msghdr, IPPROTO_SCTP, SCTP_NXTINFO,
+                sizeof(nxtinfo), &nxtinfo);
+}
+
+void sctp_ulpevent_read_nxtinfo(const struct sctp_ulpevent *event,
+                               struct msghdr *msghdr,
+                               struct sock *sk)
+{
+       struct sk_buff *skb;
+       int err;
+
+       skb = sctp_skb_recv_datagram(sk, MSG_PEEK, 1, &err);
+       if (skb != NULL) {
+               __sctp_ulpevent_read_nxtinfo(sctp_skb2event(skb),
+                                            msghdr, skb);
+               /* Just release refcount here. */
+               kfree_skb(skb);
+       }
+}
+
 /* Do accounting for bytes received and hold a reference to the association
  * for each skb.
  */