|
@@ -1602,12 +1602,13 @@ static int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
|
|
|
struct sctp_initmsg *sinit;
|
|
struct sctp_initmsg *sinit;
|
|
|
sctp_assoc_t associd = 0;
|
|
sctp_assoc_t associd = 0;
|
|
|
sctp_cmsgs_t cmsgs = { NULL };
|
|
sctp_cmsgs_t cmsgs = { NULL };
|
|
|
- int err;
|
|
|
|
|
sctp_scope_t scope;
|
|
sctp_scope_t scope;
|
|
|
- long timeo;
|
|
|
|
|
- __u16 sinfo_flags = 0;
|
|
|
|
|
|
|
+ bool fill_sinfo_ttl = false;
|
|
|
struct sctp_datamsg *datamsg;
|
|
struct sctp_datamsg *datamsg;
|
|
|
int msg_flags = msg->msg_flags;
|
|
int msg_flags = msg->msg_flags;
|
|
|
|
|
+ __u16 sinfo_flags = 0;
|
|
|
|
|
+ long timeo;
|
|
|
|
|
+ int err;
|
|
|
|
|
|
|
|
err = 0;
|
|
err = 0;
|
|
|
sp = sctp_sk(sk);
|
|
sp = sctp_sk(sk);
|
|
@@ -1648,10 +1649,21 @@ static int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
|
|
|
msg_name = msg->msg_name;
|
|
msg_name = msg->msg_name;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- sinfo = cmsgs.info;
|
|
|
|
|
sinit = cmsgs.init;
|
|
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) {
|
|
if (sinfo) {
|
|
|
sinfo_flags = sinfo->sinfo_flags;
|
|
sinfo_flags = sinfo->sinfo_flags;
|
|
|
associd = sinfo->sinfo_assoc_id;
|
|
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__);
|
|
pr_debug("%s: we have a valid association\n", __func__);
|
|
|
|
|
|
|
|
if (!sinfo) {
|
|
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));
|
|
memset(&default_sinfo, 0, sizeof(default_sinfo));
|
|
|
default_sinfo.sinfo_stream = asoc->default_stream;
|
|
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_context = asoc->default_context;
|
|
|
default_sinfo.sinfo_timetolive = asoc->default_timetolive;
|
|
default_sinfo.sinfo_timetolive = asoc->default_timetolive;
|
|
|
default_sinfo.sinfo_assoc_id = sctp_assoc2id(asoc);
|
|
default_sinfo.sinfo_assoc_id = sctp_assoc2id(asoc);
|
|
|
|
|
+
|
|
|
sinfo = &default_sinfo;
|
|
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
|
|
/* 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
|
|
* flags - flags sent or received with the user message, see Section
|
|
|
* 5 for complete description of the flags.
|
|
* 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,
|
|
static int sctp_recvmsg(struct kiocb *iocb, struct sock *sk,
|
|
|
struct msghdr *msg, size_t len, int noblock,
|
|
struct msghdr *msg, size_t len, int noblock,
|
|
|
int flags, int *addr_len)
|
|
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);
|
|
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. */
|
|
/* Check if we allow SCTP_SNDRCVINFO. */
|
|
|
if (sp->subscribe.sctp_data_io_event)
|
|
if (sp->subscribe.sctp_data_io_event)
|
|
|
sctp_ulpevent_read_sndrcvinfo(event, msg);
|
|
sctp_ulpevent_read_sndrcvinfo(event, msg);
|
|
|
|
|
+
|
|
|
#if 0
|
|
#if 0
|
|
|
/* FIXME: we should be calling IP/IPv6 layers. */
|
|
/* FIXME: we should be calling IP/IPv6 layers. */
|
|
|
if (sk->sk_protinfo.af_inet.cmsg_flags)
|
|
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))
|
|
if (copy_from_user(&sctp_sk(sk)->subscribe, optval, optlen))
|
|
|
return -EFAULT;
|
|
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
|
|
* if there is no data to be sent or retransmit, the stack will
|
|
|
* immediately send up this notification.
|
|
* immediately send up this notification.
|
|
|
*/
|
|
*/
|
|
@@ -2747,19 +2775,22 @@ static int sctp_setsockopt_default_send_param(struct sock *sk,
|
|
|
char __user *optval,
|
|
char __user *optval,
|
|
|
unsigned int optlen)
|
|
unsigned int optlen)
|
|
|
{
|
|
{
|
|
|
- struct sctp_sndrcvinfo info;
|
|
|
|
|
- struct sctp_association *asoc;
|
|
|
|
|
struct sctp_sock *sp = sctp_sk(sk);
|
|
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;
|
|
return -EINVAL;
|
|
|
if (copy_from_user(&info, optval, optlen))
|
|
if (copy_from_user(&info, optval, optlen))
|
|
|
return -EFAULT;
|
|
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);
|
|
asoc = sctp_id2assoc(sk, info.sinfo_assoc_id);
|
|
|
if (!asoc && info.sinfo_assoc_id && sctp_style(sk, UDP))
|
|
if (!asoc && info.sinfo_assoc_id && sctp_style(sk, UDP))
|
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
|
-
|
|
|
|
|
if (asoc) {
|
|
if (asoc) {
|
|
|
asoc->default_stream = info.sinfo_stream;
|
|
asoc->default_stream = info.sinfo_stream;
|
|
|
asoc->default_flags = info.sinfo_flags;
|
|
asoc->default_flags = info.sinfo_flags;
|
|
@@ -2777,6 +2808,44 @@ static int sctp_setsockopt_default_send_param(struct sock *sk,
|
|
|
return 0;
|
|
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)
|
|
/* 7.1.10 Set Primary Address (SCTP_PRIMARY_ADDR)
|
|
|
*
|
|
*
|
|
|
* Requests that the local SCTP stack use the enclosed peer address as
|
|
* 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;
|
|
return 0;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-
|
|
|
|
|
/*
|
|
/*
|
|
|
* SCTP_PEER_ADDR_THLDS
|
|
* SCTP_PEER_ADDR_THLDS
|
|
|
*
|
|
*
|
|
@@ -3574,6 +3642,38 @@ static int sctp_setsockopt_paddr_thresholds(struct sock *sk,
|
|
|
return 0;
|
|
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()
|
|
/* API 6.2 setsockopt(), getsockopt()
|
|
|
*
|
|
*
|
|
|
* Applications use setsockopt() and getsockopt() to set or retrieve
|
|
* 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,
|
|
retval = sctp_setsockopt_default_send_param(sk, optval,
|
|
|
optlen);
|
|
optlen);
|
|
|
break;
|
|
break;
|
|
|
|
|
+ case SCTP_DEFAULT_SNDINFO:
|
|
|
|
|
+ retval = sctp_setsockopt_default_sndinfo(sk, optval, optlen);
|
|
|
|
|
+ break;
|
|
|
case SCTP_PRIMARY_ADDR:
|
|
case SCTP_PRIMARY_ADDR:
|
|
|
retval = sctp_setsockopt_primary_addr(sk, optval, optlen);
|
|
retval = sctp_setsockopt_primary_addr(sk, optval, optlen);
|
|
|
break;
|
|
break;
|
|
@@ -3725,6 +3828,12 @@ static int sctp_setsockopt(struct sock *sk, int level, int optname,
|
|
|
case SCTP_PEER_ADDR_THLDS:
|
|
case SCTP_PEER_ADDR_THLDS:
|
|
|
retval = sctp_setsockopt_paddr_thresholds(sk, optval, optlen);
|
|
retval = sctp_setsockopt_paddr_thresholds(sk, optval, optlen);
|
|
|
break;
|
|
break;
|
|
|
|
|
+ case SCTP_RECVRCVINFO:
|
|
|
|
|
+ retval = sctp_setsockopt_recvrcvinfo(sk, optval, optlen);
|
|
|
|
|
+ break;
|
|
|
|
|
+ case SCTP_RECVNXTINFO:
|
|
|
|
|
+ retval = sctp_setsockopt_recvnxtinfo(sk, optval, optlen);
|
|
|
|
|
+ break;
|
|
|
default:
|
|
default:
|
|
|
retval = -ENOPROTOOPT;
|
|
retval = -ENOPROTOOPT;
|
|
|
break;
|
|
break;
|
|
@@ -3971,6 +4080,9 @@ static int sctp_init_sock(struct sock *sk)
|
|
|
/* Enable Nagle algorithm by default. */
|
|
/* Enable Nagle algorithm by default. */
|
|
|
sp->nodelay = 0;
|
|
sp->nodelay = 0;
|
|
|
|
|
|
|
|
|
|
+ sp->recvrcvinfo = 0;
|
|
|
|
|
+ sp->recvnxtinfo = 0;
|
|
|
|
|
+
|
|
|
/* Enable by default. */
|
|
/* Enable by default. */
|
|
|
sp->v4mapped = 1;
|
|
sp->v4mapped = 1;
|
|
|
|
|
|
|
@@ -4964,14 +5076,14 @@ static int sctp_getsockopt_default_send_param(struct sock *sk,
|
|
|
int len, char __user *optval,
|
|
int len, char __user *optval,
|
|
|
int __user *optlen)
|
|
int __user *optlen)
|
|
|
{
|
|
{
|
|
|
- struct sctp_sndrcvinfo info;
|
|
|
|
|
- struct sctp_association *asoc;
|
|
|
|
|
struct sctp_sock *sp = sctp_sk(sk);
|
|
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;
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
- len = sizeof(struct sctp_sndrcvinfo);
|
|
|
|
|
|
|
+ len = sizeof(info);
|
|
|
|
|
|
|
|
if (copy_from_user(&info, optval, len))
|
|
if (copy_from_user(&info, optval, len))
|
|
|
return -EFAULT;
|
|
return -EFAULT;
|
|
@@ -4979,7 +5091,6 @@ static int sctp_getsockopt_default_send_param(struct sock *sk,
|
|
|
asoc = sctp_id2assoc(sk, info.sinfo_assoc_id);
|
|
asoc = sctp_id2assoc(sk, info.sinfo_assoc_id);
|
|
|
if (!asoc && info.sinfo_assoc_id && sctp_style(sk, UDP))
|
|
if (!asoc && info.sinfo_assoc_id && sctp_style(sk, UDP))
|
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
|
-
|
|
|
|
|
if (asoc) {
|
|
if (asoc) {
|
|
|
info.sinfo_stream = asoc->default_stream;
|
|
info.sinfo_stream = asoc->default_stream;
|
|
|
info.sinfo_flags = asoc->default_flags;
|
|
info.sinfo_flags = asoc->default_flags;
|
|
@@ -5002,6 +5113,48 @@ static int sctp_getsockopt_default_send_param(struct sock *sk,
|
|
|
return 0;
|
|
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
|
|
* 7.1.5 SCTP_NODELAY
|
|
@@ -5752,6 +5905,46 @@ static int sctp_getsockopt_assoc_stats(struct sock *sk, int len,
|
|
|
return 0;
|
|
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,
|
|
static int sctp_getsockopt(struct sock *sk, int level, int optname,
|
|
|
char __user *optval, int __user *optlen)
|
|
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,
|
|
retval = sctp_getsockopt_default_send_param(sk, len,
|
|
|
optval, optlen);
|
|
optval, optlen);
|
|
|
break;
|
|
break;
|
|
|
|
|
+ case SCTP_DEFAULT_SNDINFO:
|
|
|
|
|
+ retval = sctp_getsockopt_default_sndinfo(sk, len,
|
|
|
|
|
+ optval, optlen);
|
|
|
|
|
+ break;
|
|
|
case SCTP_PRIMARY_ADDR:
|
|
case SCTP_PRIMARY_ADDR:
|
|
|
retval = sctp_getsockopt_primary_addr(sk, len, optval, optlen);
|
|
retval = sctp_getsockopt_primary_addr(sk, len, optval, optlen);
|
|
|
break;
|
|
break;
|
|
@@ -5895,6 +6092,12 @@ static int sctp_getsockopt(struct sock *sk, int level, int optname,
|
|
|
case SCTP_GET_ASSOC_STATS:
|
|
case SCTP_GET_ASSOC_STATS:
|
|
|
retval = sctp_getsockopt_assoc_stats(sk, len, optval, optlen);
|
|
retval = sctp_getsockopt_assoc_stats(sk, len, optval, optlen);
|
|
|
break;
|
|
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:
|
|
default:
|
|
|
retval = -ENOPROTOOPT;
|
|
retval = -ENOPROTOOPT;
|
|
|
break;
|
|
break;
|
|
@@ -6390,8 +6593,7 @@ static int sctp_msghdr_parse(const struct msghdr *msg, sctp_cmsgs_t *cmsgs)
|
|
|
struct cmsghdr *cmsg;
|
|
struct cmsghdr *cmsg;
|
|
|
struct msghdr *my_msg = (struct msghdr *)msg;
|
|
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)) {
|
|
cmsg = CMSG_NXTHDR(my_msg, cmsg)) {
|
|
|
if (!CMSG_OK(my_msg, cmsg))
|
|
if (!CMSG_OK(my_msg, cmsg))
|
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
@@ -6404,7 +6606,7 @@ static int sctp_msghdr_parse(const struct msghdr *msg, sctp_cmsgs_t *cmsgs)
|
|
|
switch (cmsg->cmsg_type) {
|
|
switch (cmsg->cmsg_type) {
|
|
|
case SCTP_INIT:
|
|
case SCTP_INIT:
|
|
|
/* SCTP Socket API Extension
|
|
/* 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
|
|
* This cmsghdr structure provides information for
|
|
|
* initializing new SCTP associations with sendmsg().
|
|
* 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
|
|
* 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;
|
|
return -EINVAL;
|
|
|
- cmsgs->init = (struct sctp_initmsg *)CMSG_DATA(cmsg);
|
|
|
|
|
|
|
+
|
|
|
|
|
+ cmsgs->init = CMSG_DATA(cmsg);
|
|
|
break;
|
|
break;
|
|
|
|
|
|
|
|
case SCTP_SNDRCV:
|
|
case SCTP_SNDRCV:
|
|
|
/* SCTP Socket API Extension
|
|
/* 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
|
|
* This cmsghdr structure specifies SCTP options for
|
|
|
* sendmsg() and describes SCTP header information
|
|
* 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
|
|
* 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;
|
|
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_UNORDERED | SCTP_ADDR_OVER |
|
|
|
SCTP_ABORT | SCTP_EOF))
|
|
SCTP_ABORT | SCTP_EOF))
|
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
|
break;
|
|
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:
|
|
default:
|
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
return 0;
|
|
return 0;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -6518,8 +6740,8 @@ out:
|
|
|
* Note: This is pretty much the same routine as in core/datagram.c
|
|
* Note: This is pretty much the same routine as in core/datagram.c
|
|
|
* with a few changes to make lksctp work.
|
|
* 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;
|
|
int error;
|
|
|
struct sk_buff *skb;
|
|
struct sk_buff *skb;
|