Updated lksctp-dev and lksctp-tools to follow the most recent sctpsocket draft 18 by replacing all instances of timetolive from the sctp_sndrcvinfo structure with the sinfo_pr_policy and sinfo_pr_value fields. I ran the tests in lksctp-tools and to my surprise they all pass. This is the first time I have put my dirty hands on the holy kernel source so please expect to find mistakes all over the place. I also tried to implement the SCTP_PR_SCTP_RTX policy based on the FreeBSD implementation but I am not sure were exactly I should decrement the number of retransmission counter. I do it in the sctp_retransmit_mark() method but not really sure if is correct and I am not good enough to make a test for it. Some help here would be greatly appreciated. regards, Horacio
diff --git a/net/sctp/associola.c b/net/sctp/associola.c index f4b2304..c178139 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -302,7 +302,8 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a asoc->default_ppid = sp->default_ppid; asoc->default_flags = sp->default_flags; asoc->default_context = sp->default_context; - asoc->default_timetolive = sp->default_timetolive; + asoc->default_pr_policy = sp->default_pr_policy; + asoc->default_pr_value = sp->default_pr_value; asoc->default_rcv_context = sp->default_rcv_context; /* AUTH related initializations */ diff --git a/net/sctp/chunk.c b/net/sctp/chunk.c index 1748ef9..e98cc5b 100644 --- a/net/sctp/chunk.c +++ b/net/sctp/chunk.c @@ -56,7 +56,7 @@ static void sctp_datamsg_init(struct sctp_datamsg *msg) atomic_set(&msg->refcnt, 1); msg->send_failed = 0; msg->send_error = 0; - msg->can_abandon = 0; + msg->expires_policy = SCTP_PR_SCTP_NONE; msg->expires_at = 0; INIT_LIST_HEAD(&msg->chunks); } @@ -170,13 +170,26 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc, /* Note: Calculate this outside of the loop, so that all fragments * have the same expiration. */ - if (sinfo->sinfo_timetolive) { - /* sinfo_timetolive is in milliseconds */ - msg->expires_at = jiffies + - msecs_to_jiffies(sinfo->sinfo_timetolive); - msg->can_abandon = 1; - SCTP_DEBUG_PRINTK("%s: msg:%p expires_at: %ld jiffies:%ld\n", - __func__, msg, msg->expires_at, jiffies); + + msg->expires_policy = sinfo->sinfo_pr_policy; + + if(msg->expires_policy) { + switch(msg->expires_policy) { + case SCTP_PR_SCTP_TTL: + /* sinfo_timetolive is in milliseconds */ + msg->expires_at = jiffies + + msecs_to_jiffies(sinfo->sinfo_pr_value); + SCTP_DEBUG_PRINTK("%s: msg:%p expires_at: %ld jiffies:%ld\n", + __func__, msg, msg->expires_at, jiffies); + break; + case SCTP_PR_SCTP_BUF: + break; + case SCTP_PR_SCTP_RTX: + msg->expires_at = sinfo->sinfo_pr_value; + SCTP_DEBUG_PRINTK("%s: msg:%p expires_after: %ld retransmissions \n", + __func__, msg, msg->expires_at); + break; + } } max = asoc->frag_point; @@ -291,11 +304,20 @@ int sctp_chunk_abandoned(struct sctp_chunk *chunk) { struct sctp_datamsg *msg = chunk->msg; - if (!msg->can_abandon) + if (!msg->expires_policy) return 0; - if (time_after(jiffies, msg->expires_at)) - return 1; + switch(msg->expires_policy) { + case SCTP_PR_SCTP_TTL: + if (time_after(jiffies, msg->expires_at)) + return 1; + case SCTP_PR_SCTP_BUF: + /* TODO: Implement this */ + return 0; + case SCTP_PR_SCTP_RTX: + if(msg->expires_at <= 0) + return 1; + } return 0; } diff --git a/net/sctp/output.c b/net/sctp/output.c index c3f417f..b344a9d 100644 --- a/net/sctp/output.c +++ b/net/sctp/output.c @@ -745,7 +745,7 @@ static sctp_xmit_t sctp_packet_append_data(struct sctp_packet *packet, asoc->peer.rwnd = rwnd; /* Has been accepted for transmission. */ if (!asoc->peer.prsctp_capable) - chunk->msg->can_abandon = 0; + chunk->msg->expires_policy = SCTP_PR_SCTP_NONE; finish: return retval; diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c index 247ebc9..cce5bab 100644 --- a/net/sctp/outqueue.c +++ b/net/sctp/outqueue.c @@ -462,6 +462,13 @@ void sctp_retransmit_mark(struct sctp_outq *q, transport->rto_pending = 0; } + /* If the sinfo_pr_policy is SCTP_PR_SCTP_RTX we have + * to decrement the chunk expire_at value + */ + if(chunk->msg->expires_policy == SCTP_PR_SCTP_RTX) + chunk->msg->expires_at--; + + /* Move the chunk to the retransmit queue. The chunks * on the retransmit queue are always kept in order. */ diff --git a/net/sctp/socket.c b/net/sctp/socket.c index a1b9045..1504e28 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -1698,7 +1698,8 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, default_sinfo.sinfo_flags = asoc->default_flags; default_sinfo.sinfo_ppid = asoc->default_ppid; default_sinfo.sinfo_context = asoc->default_context; - default_sinfo.sinfo_timetolive = asoc->default_timetolive; + default_sinfo.sinfo_pr_policy = asoc->default_pr_policy; + default_sinfo.sinfo_pr_value = asoc->default_pr_value; default_sinfo.sinfo_assoc_id = sctp_assoc2id(asoc); sinfo = &default_sinfo; } @@ -2539,7 +2540,7 @@ static int sctp_setsockopt_initmsg(struct sock *sk, char __user *optval, int opt * in to this call the sctp_sndrcvinfo structure defined in Section * 5.2.2) The input parameters accepted by this call include * sinfo_stream, sinfo_flags, sinfo_ppid, sinfo_context, - * sinfo_timetolive. The user must provide the sinfo_assoc_id field in + * sinfo_pr_policy. The user must provide the sinfo_assoc_id field in * to this call if the caller is using the UDP model. */ static int sctp_setsockopt_default_send_param(struct sock *sk, @@ -2563,13 +2564,15 @@ static int sctp_setsockopt_default_send_param(struct sock *sk, asoc->default_flags = info.sinfo_flags; asoc->default_ppid = info.sinfo_ppid; asoc->default_context = info.sinfo_context; - asoc->default_timetolive = info.sinfo_timetolive; + asoc->default_pr_policy = info.sinfo_pr_policy; + asoc->default_pr_value = info.sinfo_pr_value; } else { sp->default_stream = info.sinfo_stream; sp->default_flags = info.sinfo_flags; sp->default_ppid = info.sinfo_ppid; sp->default_context = info.sinfo_context; - sp->default_timetolive = info.sinfo_timetolive; + sp->default_pr_policy = info.sinfo_pr_policy; + sp->default_pr_value = info.sinfo_pr_value; } return 0; @@ -3524,7 +3527,8 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk) sp->default_ppid = 0; sp->default_flags = 0; sp->default_context = 0; - sp->default_timetolive = 0; + sp->default_pr_policy = 0; + sp->default_pr_value = 0; sp->default_rcv_context = 0; sp->max_burst = sctp_max_burst; @@ -4824,7 +4828,7 @@ static int sctp_getsockopt_adaptation_layer(struct sock *sk, int len, * in to this call the sctp_sndrcvinfo structure defined in Section * 5.2.2) The input parameters accepted by this call include * sinfo_stream, sinfo_flags, sinfo_ppid, sinfo_context, - * sinfo_timetolive. The user must provide the sinfo_assoc_id field in + * sinfo_pr_policy. The user must provide the sinfo_assoc_id field in * to this call if the caller is using the UDP model. * * For getsockopt, it get the default sctp_sndrcvinfo structure. @@ -4854,13 +4858,15 @@ static int sctp_getsockopt_default_send_param(struct sock *sk, info.sinfo_flags = asoc->default_flags; info.sinfo_ppid = asoc->default_ppid; info.sinfo_context = asoc->default_context; - info.sinfo_timetolive = asoc->default_timetolive; + info.sinfo_pr_policy = asoc->default_pr_policy; + info.sinfo_pr_value = asoc->default_pr_value; } else { info.sinfo_stream = sp->default_stream; info.sinfo_flags = sp->default_flags; info.sinfo_ppid = sp->default_ppid; info.sinfo_context = sp->default_context; - info.sinfo_timetolive = sp->default_timetolive; + info.sinfo_pr_policy = sp->default_pr_policy; + info.sinfo_pr_value = sp->default_pr_value; } if (put_user(len, optlen)) diff --git a/net/sctp/ulpevent.c b/net/sctp/ulpevent.c index 5f186ca..2c6f111 100644 --- a/net/sctp/ulpevent.c +++ b/net/sctp/ulpevent.c @@ -948,7 +948,8 @@ void sctp_ulpevent_read_sndrcvinfo(const struct sctp_ulpevent *event, sinfo.sinfo_context = event->asoc->default_rcv_context; /* These fields are not used while receiving. */ - sinfo.sinfo_timetolive = 0; + sinfo.sinfo_pr_policy = 0; + sinfo.sinfo_pr_value = 0; put_cmsg(msghdr, IPPROTO_SCTP, SCTP_SNDRCV, sizeof(struct sctp_sndrcvinfo), (void *)&sinfo);
diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 9661d7b..ffda4c5 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -208,7 +208,7 @@ extern struct sctp_globals { /* Lock that protects the local_addr_list writers */ spinlock_t addr_list_lock; - + /* Flag to indicate if addip is enabled. */ int addip_enable; int addip_noauth_enable; @@ -282,7 +282,8 @@ struct sctp_sock { __u32 default_ppid; __u16 default_flags; __u32 default_context; - __u32 default_timetolive; + __u16 default_pr_policy; + __u32 default_pr_value; __u32 default_rcv_context; int max_burst; @@ -390,7 +391,7 @@ struct sctp_cookie { /* This holds the originating address of the INIT packet. */ union sctp_addr peer_addr; - /* IG Section 2.35.3 + /* IG Section 2.35.3 * Include the source port of the INIT-ACK */ __u16 my_port; @@ -398,7 +399,7 @@ struct sctp_cookie { __u8 prsctp_capable; /* Padding for future use */ - __u8 padding; + __u8 padding; __u32 adaptation_ind; @@ -512,12 +513,12 @@ static inline __u16 sctp_ssn_next(struct sctp_stream *stream, __u16 id) } /* Skip over this ssn and all below. */ -static inline void sctp_ssn_skip(struct sctp_stream *stream, __u16 id, +static inline void sctp_ssn_skip(struct sctp_stream *stream, __u16 id, __u16 ssn) { stream->ssn[id] = ssn+1; } - + /* * Pointers to address related SCTP functions. * (i.e. things that depend on the address family.) @@ -575,7 +576,7 @@ struct sctp_af { union sctp_addr_param *, __be16 port, int iif); int (*to_addr_param) (const union sctp_addr *, - union sctp_addr_param *); + union sctp_addr_param *); int (*addr_valid) (union sctp_addr *, struct sctp_sock *, const struct sk_buff *); @@ -626,13 +627,15 @@ struct sctp_datamsg { struct list_head track; /* Reference counting. */ atomic_t refcnt; + + /* Policy used to determine expired messages */ + unsigned long expires_policy; /* When is this message no longer interesting to the peer? */ unsigned long expires_at; /* Did the messenge fail to send? */ int send_error; char send_failed; /* Control whether chunks from this message can be abandoned. */ - char can_abandon; }; struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *, @@ -1339,7 +1342,7 @@ struct sctp_endpoint { * on every receive. */ __u8 *digest; - + /* sendbuf acct. policy. */ __u32 sndbuf_policy; @@ -1759,9 +1762,10 @@ struct sctp_association { /* Default send parameters. */ __u16 default_stream; __u16 default_flags; + __u16 default_pr_policy; __u32 default_ppid; __u32 default_context; - __u32 default_timetolive; + __u32 default_pr_value; /* Default receive parameters */ __u32 default_rcv_context; diff --git a/include/net/sctp/user.h b/include/net/sctp/user.h index f205b10..c857cc5 100644 --- a/include/net/sctp/user.h +++ b/include/net/sctp/user.h @@ -71,7 +71,7 @@ enum sctp_optname { #define SCTP_NODELAY SCTP_NODELAY SCTP_AUTOCLOSE, #define SCTP_AUTOCLOSE SCTP_AUTOCLOSE - SCTP_SET_PEER_PRIMARY_ADDR, + SCTP_SET_PEER_PRIMARY_ADDR, #define SCTP_SET_PEER_PRIMARY_ADDR SCTP_SET_PEER_PRIMARY_ADDR SCTP_PRIMARY_ADDR, #define SCTP_PRIMARY_ADDR SCTP_PRIMARY_ADDR @@ -120,7 +120,7 @@ enum sctp_optname { #define SCTP_LOCAL_AUTH_CHUNKS SCTP_LOCAL_AUTH_CHUNKS - /* Internal Socket Options. Some of the sctp library functions are + /* Internal Socket Options. Some of the sctp library functions are * implemented using these socket options. */ SCTP_SOCKOPT_BINDX_ADD = 100,/* BINDX requests for adding addresses. */ @@ -183,9 +183,10 @@ struct sctp_sndrcvinfo { __u16 sinfo_stream; __u16 sinfo_ssn; __u16 sinfo_flags; + __u16 sinfo_pr_policy; __u32 sinfo_ppid; __u32 sinfo_context; - __u32 sinfo_timetolive; + __u32 sinfo_pr_value; __u32 sinfo_tsn; __u32 sinfo_cumtsn; sctp_assoc_t sinfo_assoc_id; @@ -202,9 +203,22 @@ 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_EOF=MSG_FIN, /* Initiate graceful shutdown process. */ + SCTP_EOF=MSG_FIN, /* Initiate graceful shutdown process. */ }; +/* + * sinfo_pr_policy: 16 bits (unsigned integer) + * + * This field may contain the partial reliability used to + * send the message. + */ + +enum sctp_sinfo_pr_policy { + SCTP_PR_SCTP_NONE = 0, /* Reliable transmission */ + SCTP_PR_SCTP_TTL = 1, /* Timed partial reliability */ + SCTP_PR_SCTP_BUF = 2, /* Buffer parital reliability (EXPERIMENTAL) */ + SCTP_PR_SCTP_RTX = 3, /* Retransmis partial reliability (EXPERIMENTAL) */ +}; typedef union { __u8 raw; @@ -479,7 +493,7 @@ typedef enum sctp_sn_error { * * The protocol parameters used to initialize and bound retransmission * timeout (RTO) are tunable. See [SCTP] for more information on how - * these parameters are used in RTO calculation. + * these parameters are used in RTO calculation. */ struct sctp_rtoinfo { sctp_assoc_t srto_assoc_id; @@ -717,9 +731,9 @@ struct sctp_authchunks { /* * 8.3, 8.5 get all peer/local addresses in an association. - * This parameter struct is used by SCTP_GET_PEER_ADDRS and + * This parameter struct is used by SCTP_GET_PEER_ADDRS and * SCTP_GET_LOCAL_ADDRS socket options used internally to implement - * sctp_getpaddrs() and sctp_getladdrs() API. + * sctp_getpaddrs() and sctp_getladdrs() API. */ struct sctp_getaddrs_old { sctp_assoc_t assoc_id;
diff --git a/src/func_tests/test_sctp_sendrecvmsg.c b/src/func_tests/test_sctp_sendrecvmsg.c index c275e45..d5e26d5 100644 --- a/src/func_tests/test_sctp_sendrecvmsg.c +++ b/src/func_tests/test_sctp_sendrecvmsg.c @@ -335,7 +335,8 @@ int main(int argc, char *argv[]) snd_sinfo.sinfo_ppid = rand(); snd_sinfo.sinfo_flags = 0; snd_sinfo.sinfo_stream = 2; - snd_sinfo.sinfo_timetolive = 0; + snd_sinfo.sinfo_pr_policy = SCTP_PR_SCTP_NONE; + snd_sinfo.sinfo_pr_value = 0; snd_sinfo.sinfo_assoc_id = associd1; test_sctp_send(sk1, message, strlen(message) + 1, &snd_sinfo, MSG_NOSIGNAL); diff --git a/src/func_tests/test_timetolive.c b/src/func_tests/test_timetolive.c index d9bdf1b..dc66722 100644 --- a/src/func_tests/test_timetolive.c +++ b/src/func_tests/test_timetolive.c @@ -39,14 +39,14 @@ /* * This is a basic functional test for the SCTP kernel - * implementation of sndrcvinfo.sinfo_timetolive. + * implementation of sndrcvinfo.sinfo_pr_value. * * 1) Create two sockets, the listening socket sets its RECVBUF small * 2) Create a connection. Send enough data to the non-reading listener * to fill the RCVBUF. - * 5) Set sinfo_timetolive on a message and send. - * 6) Disable sinfo_timetolive on a message and send. - * 7) Wait sinfo_timetolive. + * 5) Set sinfo_pr_value with SCTP_PR_SCTP_TTL policy on a message and send. + * 6) Disable sinfo_pr_value on a message and send. + * 7) Wait sinfo_pr_value time. * 8) Read out all the data at the receiver. * 9) Make sure timed out message did not make it. * 10) Make sure that the message with no timeout makes it to the receiver. @@ -288,7 +288,8 @@ int main(int argc, char *argv[]) outmessage.msg_name = NULL; outmessage.msg_namelen = 0; sinfo->sinfo_assoc_id = associd1; - sinfo->sinfo_timetolive = 0; + sinfo->sinfo_pr_policy = SCTP_PR_SCTP_NONE; + sinfo->sinfo_pr_value = 0; test_sendmsg(sk1, &outmessage, MSG_NOSIGNAL, gstatus.sstat_rwnd+RWND_SLOP); @@ -300,7 +301,8 @@ int main(int argc, char *argv[]) outmessage.msg_name = NULL; outmessage.msg_namelen = 0; sinfo->sinfo_assoc_id = associd1; - sinfo->sinfo_timetolive = 2000; + sinfo->sinfo_pr_policy = SCTP_PR_SCTP_TTL; + sinfo->sinfo_pr_value = 2000; test_sendmsg(sk1, &outmessage, MSG_NOSIGNAL, strlen(ttlmsg) + 1); tst_resm(TPASS, "Send a message with timeout"); @@ -313,7 +315,8 @@ int main(int argc, char *argv[]) outmessage.msg_name = NULL; outmessage.msg_namelen = 0; sinfo->sinfo_assoc_id = associd1; - sinfo->sinfo_timetolive = 0; + sinfo->sinfo_pr_policy = SCTP_PR_SCTP_NONE; + sinfo->sinfo_pr_value = 0; test_sendmsg(sk1, &outmessage, MSG_NOSIGNAL, strlen(nottlmsg)+1); tst_resm(TPASS, "Send a message with no timeout"); @@ -328,7 +331,8 @@ int main(int argc, char *argv[]) outmessage.msg_name = NULL; outmessage.msg_namelen = 0; sinfo->sinfo_assoc_id = associd1; - sinfo->sinfo_timetolive = 2000; + sinfo->sinfo_pr_policy = SCTP_PR_SCTP_TTL; + sinfo->sinfo_pr_value = 2000; test_sendmsg(sk1, &outmessage, MSG_NOSIGNAL, sizeof(ttlfrag)); tst_resm(TPASS, "Send a fragmented message with timeout"); diff --git a/src/include/netinet/sctp.h b/src/include/netinet/sctp.h index ae557a5..53454db 100644 --- a/src/include/netinet/sctp.h +++ b/src/include/netinet/sctp.h @@ -184,9 +184,10 @@ struct sctp_sndrcvinfo { __u16 sinfo_stream; __u16 sinfo_ssn; __u16 sinfo_flags; + __u16 sinfo_pr_policy; __u32 sinfo_ppid; __u32 sinfo_context; - __u32 sinfo_timetolive; + __u32 sinfo_pr_value; __u32 sinfo_tsn; __u32 sinfo_cumtsn; sctp_assoc_t sinfo_assoc_id; @@ -206,6 +207,17 @@ enum sctp_sinfo_flags { SCTP_EOF=MSG_FIN, /* Initiate graceful shutdown process. */ }; +/* + * sinfo_pr_policy: 16 bits (unsigned integer) + * + */ + +enum sctp_sinfo_pr_policy { + SCTP_PR_SCTP_NONE = 0, /* Reliable transmission */ + SCTP_PR_SCTP_TTL = 1, /* Timed partial reliable */ + SCTP_PR_SCTP_BUF = 2, /* Buffer parital reliable */ + SCTP_PR_SCTP_RTX = 3, /* Retransmist partial reliable */ +}; typedef union { __u8 raw; @@ -816,7 +828,7 @@ int sctp_freeladdrs(struct sockaddr *addrs); */ int sctp_sendmsg(int s, const void *msg, size_t len, struct sockaddr *to, socklen_t tolen, uint32_t ppid, uint32_t flags, - uint16_t stream_no, uint32_t timetolive, uint32_t context); + uint16_t stream_no, uint32_t pr_value, uint32_t context); /* This library function assist the user with sending a message without * dealing directly with the CMSG header. diff --git a/src/lib/sendmsg.c b/src/lib/sendmsg.c index 1de592d..2b6b46e 100644 --- a/src/lib/sendmsg.c +++ b/src/lib/sendmsg.c @@ -31,7 +31,7 @@ int sctp_sendmsg(int s, const void *msg, size_t len, struct sockaddr *to, socklen_t tolen, uint32_t ppid, uint32_t flags, - uint16_t stream_no, uint32_t timetolive, uint32_t context) + uint16_t stream_no, uint32_t pr_value, uint32_t context) { struct msghdr outmsg; struct iovec iov; @@ -61,9 +61,16 @@ sctp_sendmsg(int s, const void *msg, size_t len, struct sockaddr *to, sinfo->sinfo_ppid = ppid; sinfo->sinfo_flags = flags; sinfo->sinfo_stream = stream_no; - sinfo->sinfo_timetolive = timetolive; sinfo->sinfo_context = context; + /* This is needed for the test_sctp_sendrecv to pass */ + sinfo->sinfo_pr_value = pr_value; + if(sinfo->sinfo_pr_value > 0) { + sinfo->sinfo_pr_policy = SCTP_PR_SCTP_TTL; + } else { + sinfo->sinfo_pr_policy = SCTP_PR_SCTP_NONE; + } + return sendmsg(s, &outmsg, 0); } diff --git a/src/testlib/sctputil.h b/src/testlib/sctputil.h index 347c91b..0afbc36 100644 --- a/src/testlib/sctputil.h +++ b/src/testlib/sctputil.h @@ -270,11 +270,11 @@ static inline int test_sctp_peeloff(int sk, sctp_assoc_t assoc_id) static inline int test_sctp_sendmsg(int s, const void *msg, size_t len, struct sockaddr *to, socklen_t tolen, uint32_t ppid, uint32_t flags, - uint16_t stream_no, uint32_t timetolive, + uint16_t stream_no, uint32_t pr_value, uint32_t context) { int error = sctp_sendmsg(s, msg, len, to, tolen, ppid, flags, stream_no, - timetolive, context); + pr_value, context); if (len != error) tst_brkm(TBROK, tst_exit, "sctp_sendmsg: error:%d errno:%d", error, errno);