Merge branch 'sctp-rfc7496-support'
Xin Long says: ==================== sctp: implement rfc7496 in sctp This patchset implements "Additional Policies for the Partially Reliable Stream Control Transmission Protocol Extension" described on RFC7496. The Partially Reliable SCTP (PR-SCTP) extension defined in [RFC3758] provides a generic method for senders to abandon user messages. The decision to abandon a user message is sender side only, and the exact condition is called a "PR-SCTP policy". This patchset implements 3 policies: 1. Timed Reliability: This allows the sender to specify a timeout for a user message after which the SCTP stack abandons the user message. 2. Limited Retransmission Policy: Allows limitation of the number of retransmissions. 3. Priority Policy: Allows removal of lower-priority messages if space for higher-priority messages is needed in the send buffer. Patch 1-3 add some sockopts in sctp to set/get pr_sctp policy status. Patch 4-6 implement these 3 policies one by one. ==================== Acked-by: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
4154cb40e7
|
@ -602,6 +602,16 @@ struct sctp_chunk {
|
|||
/* This needs to be recoverable for SCTP_SEND_FAILED events. */
|
||||
struct sctp_sndrcvinfo sinfo;
|
||||
|
||||
/* We use this field to record param for prsctp policies,
|
||||
* for TTL policy, it is the time_to_drop of this chunk,
|
||||
* for RTX policy, it is the max_sent_count of this chunk,
|
||||
* for PRIO policy, it is the priority of this chunk.
|
||||
*/
|
||||
unsigned long prsctp_param;
|
||||
|
||||
/* How many times this chunk have been sent, for prsctp RTX policy */
|
||||
int sent_count;
|
||||
|
||||
/* Which association does this belong to? */
|
||||
struct sctp_association *asoc;
|
||||
|
||||
|
@ -1074,6 +1084,8 @@ void sctp_retransmit(struct sctp_outq *, struct sctp_transport *,
|
|||
sctp_retransmit_reason_t);
|
||||
void sctp_retransmit_mark(struct sctp_outq *, struct sctp_transport *, __u8);
|
||||
int sctp_outq_uncork(struct sctp_outq *, gfp_t gfp);
|
||||
void sctp_prsctp_prune(struct sctp_association *asoc,
|
||||
struct sctp_sndrcvinfo *sinfo, int msg_len);
|
||||
/* Uncork and flush an outqueue. */
|
||||
static inline void sctp_outq_cork(struct sctp_outq *q)
|
||||
{
|
||||
|
@ -1256,7 +1268,8 @@ struct sctp_endpoint {
|
|||
/* SCTP-AUTH: endpoint shared keys */
|
||||
struct list_head endpoint_shared_keys;
|
||||
__u16 active_key_id;
|
||||
__u8 auth_enable;
|
||||
__u8 auth_enable:1,
|
||||
prsctp_enable:1;
|
||||
};
|
||||
|
||||
/* Recover the outter endpoint structure. */
|
||||
|
@ -1848,9 +1861,15 @@ struct sctp_association {
|
|||
__u16 active_key_id;
|
||||
|
||||
__u8 need_ecne:1, /* Need to send an ECNE Chunk? */
|
||||
temp:1; /* Is it a temporary association? */
|
||||
temp:1, /* Is it a temporary association? */
|
||||
prsctp_enable:1;
|
||||
|
||||
struct sctp_priv_assoc_stats stats;
|
||||
|
||||
int sent_cnt_removable;
|
||||
|
||||
__u64 abandoned_unsent[SCTP_PR_INDEX(MAX) + 1];
|
||||
__u64 abandoned_sent[SCTP_PR_INDEX(MAX) + 1];
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -112,6 +112,31 @@ typedef __s32 sctp_assoc_t;
|
|||
#define SCTP_SOCKOPT_CONNECTX 110 /* CONNECTX requests. */
|
||||
#define SCTP_SOCKOPT_CONNECTX3 111 /* CONNECTX requests (updated) */
|
||||
#define SCTP_GET_ASSOC_STATS 112 /* Read only */
|
||||
#define SCTP_PR_SUPPORTED 113
|
||||
#define SCTP_DEFAULT_PRINFO 114
|
||||
#define SCTP_PR_ASSOC_STATUS 115
|
||||
|
||||
/* PR-SCTP policies */
|
||||
#define SCTP_PR_SCTP_NONE 0x0000
|
||||
#define SCTP_PR_SCTP_TTL 0x0010
|
||||
#define SCTP_PR_SCTP_RTX 0x0020
|
||||
#define SCTP_PR_SCTP_PRIO 0x0030
|
||||
#define SCTP_PR_SCTP_MAX SCTP_PR_SCTP_PRIO
|
||||
#define SCTP_PR_SCTP_MASK 0x0030
|
||||
|
||||
#define __SCTP_PR_INDEX(x) ((x >> 4) - 1)
|
||||
#define SCTP_PR_INDEX(x) __SCTP_PR_INDEX(SCTP_PR_SCTP_ ## x)
|
||||
|
||||
#define SCTP_PR_POLICY(x) ((x) & SCTP_PR_SCTP_MASK)
|
||||
#define SCTP_PR_SET_POLICY(flags, x) \
|
||||
do { \
|
||||
flags &= ~SCTP_PR_SCTP_MASK; \
|
||||
flags |= x; \
|
||||
} while (0)
|
||||
|
||||
#define SCTP_PR_TTL_ENABLED(x) (SCTP_PR_POLICY(x) == SCTP_PR_SCTP_TTL)
|
||||
#define SCTP_PR_RTX_ENABLED(x) (SCTP_PR_POLICY(x) == SCTP_PR_SCTP_RTX)
|
||||
#define SCTP_PR_PRIO_ENABLED(x) (SCTP_PR_POLICY(x) == SCTP_PR_SCTP_PRIO)
|
||||
|
||||
/* 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. */
|
||||
|
@ -902,4 +927,21 @@ struct sctp_paddrthlds {
|
|||
__u16 spt_pathpfthld;
|
||||
};
|
||||
|
||||
/*
|
||||
* Socket Option for Getting the Association/Stream-Specific PR-SCTP Status
|
||||
*/
|
||||
struct sctp_prstatus {
|
||||
sctp_assoc_t sprstat_assoc_id;
|
||||
__u16 sprstat_sid;
|
||||
__u16 sprstat_policy;
|
||||
__u64 sprstat_abandoned_unsent;
|
||||
__u64 sprstat_abandoned_sent;
|
||||
};
|
||||
|
||||
struct sctp_default_prinfo {
|
||||
sctp_assoc_t pr_assoc_id;
|
||||
__u32 pr_value;
|
||||
__u16 pr_policy;
|
||||
};
|
||||
|
||||
#endif /* _UAPI_SCTP_H */
|
||||
|
|
|
@ -268,6 +268,7 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a
|
|||
goto fail_init;
|
||||
|
||||
asoc->active_key_id = ep->active_key_id;
|
||||
asoc->prsctp_enable = ep->prsctp_enable;
|
||||
|
||||
/* Save the hmacs and chunks list into this association */
|
||||
if (ep->auth_hmacs_list)
|
||||
|
|
|
@ -335,13 +335,32 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc,
|
|||
/* Check whether this message has expired. */
|
||||
int sctp_chunk_abandoned(struct sctp_chunk *chunk)
|
||||
{
|
||||
struct sctp_datamsg *msg = chunk->msg;
|
||||
if (!chunk->asoc->prsctp_enable ||
|
||||
!SCTP_PR_POLICY(chunk->sinfo.sinfo_flags)) {
|
||||
struct sctp_datamsg *msg = chunk->msg;
|
||||
|
||||
if (!msg->can_abandon)
|
||||
return 0;
|
||||
|
||||
if (time_after(jiffies, msg->expires_at))
|
||||
return 1;
|
||||
|
||||
if (!msg->can_abandon)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (time_after(jiffies, msg->expires_at))
|
||||
if (SCTP_PR_TTL_ENABLED(chunk->sinfo.sinfo_flags) &&
|
||||
time_after(jiffies, chunk->prsctp_param)) {
|
||||
if (chunk->sent_count)
|
||||
chunk->asoc->abandoned_sent[SCTP_PR_INDEX(TTL)]++;
|
||||
else
|
||||
chunk->asoc->abandoned_unsent[SCTP_PR_INDEX(TTL)]++;
|
||||
return 1;
|
||||
} else if (SCTP_PR_RTX_ENABLED(chunk->sinfo.sinfo_flags) &&
|
||||
chunk->sent_count > chunk->prsctp_param) {
|
||||
chunk->asoc->abandoned_sent[SCTP_PR_INDEX(RTX)]++;
|
||||
return 1;
|
||||
}
|
||||
/* PRIO policy is processed by sendmsg, not here */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -163,6 +163,7 @@ static struct sctp_endpoint *sctp_endpoint_init(struct sctp_endpoint *ep,
|
|||
*/
|
||||
ep->auth_hmacs_list = auth_hmacs;
|
||||
ep->auth_chunk_list = auth_chunks;
|
||||
ep->prsctp_enable = net->sctp.prsctp_enable;
|
||||
|
||||
return ep;
|
||||
|
||||
|
|
|
@ -316,6 +316,8 @@ static sctp_xmit_t __sctp_packet_append_chunk(struct sctp_packet *packet,
|
|||
packet->has_data = 1;
|
||||
/* timestamp the chunk for rtx purposes */
|
||||
chunk->sent_at = jiffies;
|
||||
/* Mainly used for prsctp RTX policy */
|
||||
chunk->sent_count++;
|
||||
break;
|
||||
case SCTP_CID_COOKIE_ECHO:
|
||||
packet->has_cookie_echo = 1;
|
||||
|
|
|
@ -326,6 +326,9 @@ int sctp_outq_tail(struct sctp_outq *q, struct sctp_chunk *chunk, gfp_t gfp)
|
|||
|
||||
sctp_chunk_hold(chunk);
|
||||
sctp_outq_tail_data(q, chunk);
|
||||
if (chunk->asoc->prsctp_enable &&
|
||||
SCTP_PR_PRIO_ENABLED(chunk->sinfo.sinfo_flags))
|
||||
chunk->asoc->sent_cnt_removable++;
|
||||
if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED)
|
||||
SCTP_INC_STATS(net, SCTP_MIB_OUTUNORDERCHUNKS);
|
||||
else
|
||||
|
@ -372,6 +375,96 @@ static void sctp_insert_list(struct list_head *head, struct list_head *new)
|
|||
list_add_tail(new, head);
|
||||
}
|
||||
|
||||
static int sctp_prsctp_prune_sent(struct sctp_association *asoc,
|
||||
struct sctp_sndrcvinfo *sinfo,
|
||||
struct list_head *queue, int msg_len)
|
||||
{
|
||||
struct sctp_chunk *chk, *temp;
|
||||
|
||||
list_for_each_entry_safe(chk, temp, queue, transmitted_list) {
|
||||
if (!SCTP_PR_PRIO_ENABLED(chk->sinfo.sinfo_flags) ||
|
||||
chk->prsctp_param <= sinfo->sinfo_timetolive)
|
||||
continue;
|
||||
|
||||
list_del_init(&chk->transmitted_list);
|
||||
sctp_insert_list(&asoc->outqueue.abandoned,
|
||||
&chk->transmitted_list);
|
||||
|
||||
asoc->sent_cnt_removable--;
|
||||
asoc->abandoned_sent[SCTP_PR_INDEX(PRIO)]++;
|
||||
|
||||
if (!chk->tsn_gap_acked) {
|
||||
if (chk->transport)
|
||||
chk->transport->flight_size -=
|
||||
sctp_data_size(chk);
|
||||
asoc->outqueue.outstanding_bytes -= sctp_data_size(chk);
|
||||
}
|
||||
|
||||
msg_len -= SCTP_DATA_SNDSIZE(chk) +
|
||||
sizeof(struct sk_buff) +
|
||||
sizeof(struct sctp_chunk);
|
||||
if (msg_len <= 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return msg_len;
|
||||
}
|
||||
|
||||
static int sctp_prsctp_prune_unsent(struct sctp_association *asoc,
|
||||
struct sctp_sndrcvinfo *sinfo,
|
||||
struct list_head *queue, int msg_len)
|
||||
{
|
||||
struct sctp_chunk *chk, *temp;
|
||||
|
||||
list_for_each_entry_safe(chk, temp, queue, list) {
|
||||
if (!SCTP_PR_PRIO_ENABLED(chk->sinfo.sinfo_flags) ||
|
||||
chk->prsctp_param <= sinfo->sinfo_timetolive)
|
||||
continue;
|
||||
|
||||
list_del_init(&chk->list);
|
||||
asoc->sent_cnt_removable--;
|
||||
asoc->abandoned_unsent[SCTP_PR_INDEX(PRIO)]++;
|
||||
|
||||
msg_len -= SCTP_DATA_SNDSIZE(chk) +
|
||||
sizeof(struct sk_buff) +
|
||||
sizeof(struct sctp_chunk);
|
||||
sctp_chunk_free(chk);
|
||||
if (msg_len <= 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return msg_len;
|
||||
}
|
||||
|
||||
/* Abandon the chunks according their priorities */
|
||||
void sctp_prsctp_prune(struct sctp_association *asoc,
|
||||
struct sctp_sndrcvinfo *sinfo, int msg_len)
|
||||
{
|
||||
struct sctp_transport *transport;
|
||||
|
||||
if (!asoc->prsctp_enable || !asoc->sent_cnt_removable)
|
||||
return;
|
||||
|
||||
msg_len = sctp_prsctp_prune_sent(asoc, sinfo,
|
||||
&asoc->outqueue.retransmit,
|
||||
msg_len);
|
||||
if (msg_len <= 0)
|
||||
return;
|
||||
|
||||
list_for_each_entry(transport, &asoc->peer.transport_addr_list,
|
||||
transports) {
|
||||
msg_len = sctp_prsctp_prune_sent(asoc, sinfo,
|
||||
&transport->transmitted,
|
||||
msg_len);
|
||||
if (msg_len <= 0)
|
||||
return;
|
||||
}
|
||||
|
||||
sctp_prsctp_prune_unsent(asoc, sinfo,
|
||||
&asoc->outqueue.out_chunk_list,
|
||||
msg_len);
|
||||
}
|
||||
|
||||
/* Mark all the eligible packets on a transport for retransmission. */
|
||||
void sctp_retransmit_mark(struct sctp_outq *q,
|
||||
struct sctp_transport *transport,
|
||||
|
@ -962,6 +1055,9 @@ static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout, gfp_t gfp)
|
|||
|
||||
/* Mark as failed send. */
|
||||
sctp_chunk_fail(chunk, SCTP_ERROR_INV_STRM);
|
||||
if (asoc->prsctp_enable &&
|
||||
SCTP_PR_PRIO_ENABLED(chunk->sinfo.sinfo_flags))
|
||||
asoc->sent_cnt_removable--;
|
||||
sctp_chunk_free(chunk);
|
||||
continue;
|
||||
}
|
||||
|
@ -1251,6 +1347,9 @@ int sctp_outq_sack(struct sctp_outq *q, struct sctp_chunk *chunk)
|
|||
tsn = ntohl(tchunk->subh.data_hdr->tsn);
|
||||
if (TSN_lte(tsn, ctsn)) {
|
||||
list_del_init(&tchunk->transmitted_list);
|
||||
if (asoc->prsctp_enable &&
|
||||
SCTP_PR_PRIO_ENABLED(chunk->sinfo.sinfo_flags))
|
||||
asoc->sent_cnt_removable--;
|
||||
sctp_chunk_free(tchunk);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -261,7 +261,7 @@ struct sctp_chunk *sctp_make_init(const struct sctp_association *asoc,
|
|||
chunksize += WORD_ROUND(SCTP_SAT_LEN(num_types));
|
||||
chunksize += sizeof(ecap_param);
|
||||
|
||||
if (net->sctp.prsctp_enable)
|
||||
if (asoc->prsctp_enable)
|
||||
chunksize += sizeof(prsctp_param);
|
||||
|
||||
/* ADDIP: Section 4.2.7:
|
||||
|
@ -355,7 +355,7 @@ struct sctp_chunk *sctp_make_init(const struct sctp_association *asoc,
|
|||
sctp_addto_param(retval, num_ext, extensions);
|
||||
}
|
||||
|
||||
if (net->sctp.prsctp_enable)
|
||||
if (asoc->prsctp_enable)
|
||||
sctp_addto_chunk(retval, sizeof(prsctp_param), &prsctp_param);
|
||||
|
||||
if (sp->adaptation_ind) {
|
||||
|
@ -711,6 +711,20 @@ struct sctp_chunk *sctp_make_ecne(const struct sctp_association *asoc,
|
|||
return retval;
|
||||
}
|
||||
|
||||
static void sctp_set_prsctp_policy(struct sctp_chunk *chunk,
|
||||
const struct sctp_sndrcvinfo *sinfo)
|
||||
{
|
||||
if (!chunk->asoc->prsctp_enable)
|
||||
return;
|
||||
|
||||
if (SCTP_PR_TTL_ENABLED(sinfo->sinfo_flags))
|
||||
chunk->prsctp_param =
|
||||
jiffies + msecs_to_jiffies(sinfo->sinfo_timetolive);
|
||||
else if (SCTP_PR_RTX_ENABLED(sinfo->sinfo_flags) ||
|
||||
SCTP_PR_PRIO_ENABLED(sinfo->sinfo_flags))
|
||||
chunk->prsctp_param = sinfo->sinfo_timetolive;
|
||||
}
|
||||
|
||||
/* Make a DATA chunk for the given association from the provided
|
||||
* parameters. However, do not populate the data payload.
|
||||
*/
|
||||
|
@ -744,6 +758,7 @@ struct sctp_chunk *sctp_make_datafrag_empty(struct sctp_association *asoc,
|
|||
|
||||
retval->subh.data_hdr = sctp_addto_chunk(retval, sizeof(dp), &dp);
|
||||
memcpy(&retval->sinfo, sinfo, sizeof(struct sctp_sndrcvinfo));
|
||||
sctp_set_prsctp_policy(retval, sinfo);
|
||||
|
||||
nodata:
|
||||
return retval;
|
||||
|
@ -2024,8 +2039,8 @@ static void sctp_process_ext_param(struct sctp_association *asoc,
|
|||
for (i = 0; i < num_ext; i++) {
|
||||
switch (param.ext->chunks[i]) {
|
||||
case SCTP_CID_FWD_TSN:
|
||||
if (net->sctp.prsctp_enable && !asoc->peer.prsctp_capable)
|
||||
asoc->peer.prsctp_capable = 1;
|
||||
if (asoc->prsctp_enable && !asoc->peer.prsctp_capable)
|
||||
asoc->peer.prsctp_capable = 1;
|
||||
break;
|
||||
case SCTP_CID_AUTH:
|
||||
/* if the peer reports AUTH, assume that he
|
||||
|
@ -2169,7 +2184,7 @@ static sctp_ierror_t sctp_verify_param(struct net *net,
|
|||
break;
|
||||
|
||||
case SCTP_PARAM_FWD_TSN_SUPPORT:
|
||||
if (net->sctp.prsctp_enable)
|
||||
if (ep->prsctp_enable)
|
||||
break;
|
||||
goto fallthrough;
|
||||
|
||||
|
@ -2653,7 +2668,7 @@ static int sctp_process_param(struct sctp_association *asoc,
|
|||
break;
|
||||
|
||||
case SCTP_PARAM_FWD_TSN_SUPPORT:
|
||||
if (net->sctp.prsctp_enable) {
|
||||
if (asoc->prsctp_enable) {
|
||||
asoc->peer.prsctp_capable = 1;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -1914,6 +1914,9 @@ static int sctp_sendmsg(struct sock *sk, struct msghdr *msg, size_t msg_len)
|
|||
goto out_free;
|
||||
}
|
||||
|
||||
if (sctp_wspace(asoc) < msg_len)
|
||||
sctp_prsctp_prune(asoc, sinfo, msg_len - sctp_wspace(asoc));
|
||||
|
||||
timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT);
|
||||
if (!sctp_wspace(asoc)) {
|
||||
err = sctp_wait_for_sndbuf(asoc, &timeo, msg_len);
|
||||
|
@ -3661,6 +3664,80 @@ static int sctp_setsockopt_recvnxtinfo(struct sock *sk,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int sctp_setsockopt_pr_supported(struct sock *sk,
|
||||
char __user *optval,
|
||||
unsigned int optlen)
|
||||
{
|
||||
struct sctp_assoc_value params;
|
||||
struct sctp_association *asoc;
|
||||
int retval = -EINVAL;
|
||||
|
||||
if (optlen != sizeof(params))
|
||||
goto out;
|
||||
|
||||
if (copy_from_user(¶ms, optval, optlen)) {
|
||||
retval = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
asoc = sctp_id2assoc(sk, params.assoc_id);
|
||||
if (asoc) {
|
||||
asoc->prsctp_enable = !!params.assoc_value;
|
||||
} else if (!params.assoc_id) {
|
||||
struct sctp_sock *sp = sctp_sk(sk);
|
||||
|
||||
sp->ep->prsctp_enable = !!params.assoc_value;
|
||||
} else {
|
||||
goto out;
|
||||
}
|
||||
|
||||
retval = 0;
|
||||
|
||||
out:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int sctp_setsockopt_default_prinfo(struct sock *sk,
|
||||
char __user *optval,
|
||||
unsigned int optlen)
|
||||
{
|
||||
struct sctp_default_prinfo info;
|
||||
struct sctp_association *asoc;
|
||||
int retval = -EINVAL;
|
||||
|
||||
if (optlen != sizeof(info))
|
||||
goto out;
|
||||
|
||||
if (copy_from_user(&info, optval, sizeof(info))) {
|
||||
retval = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (info.pr_policy & ~SCTP_PR_SCTP_MASK)
|
||||
goto out;
|
||||
|
||||
if (info.pr_policy == SCTP_PR_SCTP_NONE)
|
||||
info.pr_value = 0;
|
||||
|
||||
asoc = sctp_id2assoc(sk, info.pr_assoc_id);
|
||||
if (asoc) {
|
||||
SCTP_PR_SET_POLICY(asoc->default_flags, info.pr_policy);
|
||||
asoc->default_timetolive = info.pr_value;
|
||||
} else if (!info.pr_assoc_id) {
|
||||
struct sctp_sock *sp = sctp_sk(sk);
|
||||
|
||||
SCTP_PR_SET_POLICY(sp->default_flags, info.pr_policy);
|
||||
sp->default_timetolive = info.pr_value;
|
||||
} else {
|
||||
goto out;
|
||||
}
|
||||
|
||||
retval = 0;
|
||||
|
||||
out:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* API 6.2 setsockopt(), getsockopt()
|
||||
*
|
||||
* Applications use setsockopt() and getsockopt() to set or retrieve
|
||||
|
@ -3821,6 +3898,12 @@ static int sctp_setsockopt(struct sock *sk, int level, int optname,
|
|||
case SCTP_RECVNXTINFO:
|
||||
retval = sctp_setsockopt_recvnxtinfo(sk, optval, optlen);
|
||||
break;
|
||||
case SCTP_PR_SUPPORTED:
|
||||
retval = sctp_setsockopt_pr_supported(sk, optval, optlen);
|
||||
break;
|
||||
case SCTP_DEFAULT_PRINFO:
|
||||
retval = sctp_setsockopt_default_prinfo(sk, optval, optlen);
|
||||
break;
|
||||
default:
|
||||
retval = -ENOPROTOOPT;
|
||||
break;
|
||||
|
@ -6166,6 +6249,148 @@ static int sctp_getsockopt_recvnxtinfo(struct sock *sk, int len,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int sctp_getsockopt_pr_supported(struct sock *sk, int len,
|
||||
char __user *optval,
|
||||
int __user *optlen)
|
||||
{
|
||||
struct sctp_assoc_value params;
|
||||
struct sctp_association *asoc;
|
||||
int retval = -EFAULT;
|
||||
|
||||
if (len < sizeof(params)) {
|
||||
retval = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
len = sizeof(params);
|
||||
if (copy_from_user(¶ms, optval, len))
|
||||
goto out;
|
||||
|
||||
asoc = sctp_id2assoc(sk, params.assoc_id);
|
||||
if (asoc) {
|
||||
params.assoc_value = asoc->prsctp_enable;
|
||||
} else if (!params.assoc_id) {
|
||||
struct sctp_sock *sp = sctp_sk(sk);
|
||||
|
||||
params.assoc_value = sp->ep->prsctp_enable;
|
||||
} else {
|
||||
retval = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (put_user(len, optlen))
|
||||
goto out;
|
||||
|
||||
if (copy_to_user(optval, ¶ms, len))
|
||||
goto out;
|
||||
|
||||
retval = 0;
|
||||
|
||||
out:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int sctp_getsockopt_default_prinfo(struct sock *sk, int len,
|
||||
char __user *optval,
|
||||
int __user *optlen)
|
||||
{
|
||||
struct sctp_default_prinfo info;
|
||||
struct sctp_association *asoc;
|
||||
int retval = -EFAULT;
|
||||
|
||||
if (len < sizeof(info)) {
|
||||
retval = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
len = sizeof(info);
|
||||
if (copy_from_user(&info, optval, len))
|
||||
goto out;
|
||||
|
||||
asoc = sctp_id2assoc(sk, info.pr_assoc_id);
|
||||
if (asoc) {
|
||||
info.pr_policy = SCTP_PR_POLICY(asoc->default_flags);
|
||||
info.pr_value = asoc->default_timetolive;
|
||||
} else if (!info.pr_assoc_id) {
|
||||
struct sctp_sock *sp = sctp_sk(sk);
|
||||
|
||||
info.pr_policy = SCTP_PR_POLICY(sp->default_flags);
|
||||
info.pr_value = sp->default_timetolive;
|
||||
} else {
|
||||
retval = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (put_user(len, optlen))
|
||||
goto out;
|
||||
|
||||
if (copy_to_user(optval, &info, len))
|
||||
goto out;
|
||||
|
||||
retval = 0;
|
||||
|
||||
out:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int sctp_getsockopt_pr_assocstatus(struct sock *sk, int len,
|
||||
char __user *optval,
|
||||
int __user *optlen)
|
||||
{
|
||||
struct sctp_prstatus params;
|
||||
struct sctp_association *asoc;
|
||||
int policy;
|
||||
int retval = -EINVAL;
|
||||
|
||||
if (len < sizeof(params))
|
||||
goto out;
|
||||
|
||||
len = sizeof(params);
|
||||
if (copy_from_user(¶ms, optval, len)) {
|
||||
retval = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
policy = params.sprstat_policy;
|
||||
if (policy & ~SCTP_PR_SCTP_MASK)
|
||||
goto out;
|
||||
|
||||
asoc = sctp_id2assoc(sk, params.sprstat_assoc_id);
|
||||
if (!asoc)
|
||||
goto out;
|
||||
|
||||
if (policy == SCTP_PR_SCTP_NONE) {
|
||||
params.sprstat_abandoned_unsent = 0;
|
||||
params.sprstat_abandoned_sent = 0;
|
||||
for (policy = 0; policy <= SCTP_PR_INDEX(MAX); policy++) {
|
||||
params.sprstat_abandoned_unsent +=
|
||||
asoc->abandoned_unsent[policy];
|
||||
params.sprstat_abandoned_sent +=
|
||||
asoc->abandoned_sent[policy];
|
||||
}
|
||||
} else {
|
||||
params.sprstat_abandoned_unsent =
|
||||
asoc->abandoned_unsent[__SCTP_PR_INDEX(policy)];
|
||||
params.sprstat_abandoned_sent =
|
||||
asoc->abandoned_sent[__SCTP_PR_INDEX(policy)];
|
||||
}
|
||||
|
||||
if (put_user(len, optlen)) {
|
||||
retval = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (copy_to_user(optval, ¶ms, len)) {
|
||||
retval = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
retval = 0;
|
||||
|
||||
out:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int sctp_getsockopt(struct sock *sk, int level, int optname,
|
||||
char __user *optval, int __user *optlen)
|
||||
{
|
||||
|
@ -6319,6 +6544,17 @@ static int sctp_getsockopt(struct sock *sk, int level, int optname,
|
|||
case SCTP_RECVNXTINFO:
|
||||
retval = sctp_getsockopt_recvnxtinfo(sk, len, optval, optlen);
|
||||
break;
|
||||
case SCTP_PR_SUPPORTED:
|
||||
retval = sctp_getsockopt_pr_supported(sk, len, optval, optlen);
|
||||
break;
|
||||
case SCTP_DEFAULT_PRINFO:
|
||||
retval = sctp_getsockopt_default_prinfo(sk, len, optval,
|
||||
optlen);
|
||||
break;
|
||||
case SCTP_PR_ASSOC_STATUS:
|
||||
retval = sctp_getsockopt_pr_assocstatus(sk, len, optval,
|
||||
optlen);
|
||||
break;
|
||||
default:
|
||||
retval = -ENOPROTOOPT;
|
||||
break;
|
||||
|
@ -6866,7 +7102,7 @@ static int sctp_msghdr_parse(const struct msghdr *msg, sctp_cmsgs_t *cmsgs)
|
|||
|
||||
if (cmsgs->srinfo->sinfo_flags &
|
||||
~(SCTP_UNORDERED | SCTP_ADDR_OVER |
|
||||
SCTP_SACK_IMMEDIATELY |
|
||||
SCTP_SACK_IMMEDIATELY | SCTP_PR_SCTP_MASK |
|
||||
SCTP_ABORT | SCTP_EOF))
|
||||
return -EINVAL;
|
||||
break;
|
||||
|
@ -6890,7 +7126,7 @@ static int sctp_msghdr_parse(const struct msghdr *msg, sctp_cmsgs_t *cmsgs)
|
|||
|
||||
if (cmsgs->sinfo->snd_flags &
|
||||
~(SCTP_UNORDERED | SCTP_ADDR_OVER |
|
||||
SCTP_SACK_IMMEDIATELY |
|
||||
SCTP_SACK_IMMEDIATELY | SCTP_PR_SCTP_MASK |
|
||||
SCTP_ABORT | SCTP_EOF))
|
||||
return -EINVAL;
|
||||
break;
|
||||
|
|
Loading…
Reference in New Issue