- added a new option for forcing send retries by attempting to re-open
the association (useful for example when a peer reboots or performs a
failover to another host, to avoid loosing the current association send
queue)
- fix: SCTP_UNORDERED properly set
... | ... |
@@ -36,6 +36,7 @@ void init_sctp_options() |
36 | 36 |
#ifdef USE_SCTP |
37 | 37 |
sctp_options.sctp_autoclose=DEFAULT_SCTP_AUTOCLOSE; /* in seconds */ |
38 | 38 |
sctp_options.sctp_send_ttl=DEFAULT_SCTP_SEND_TTL; /* in milliseconds */ |
39 |
+ sctp_options.sctp_send_retries=DEFAULT_SCTP_SEND_RETRIES; |
|
39 | 40 |
#endif |
40 | 41 |
} |
41 | 42 |
|
... | ... |
@@ -55,6 +56,12 @@ void sctp_options_check() |
55 | 55 |
#ifndef USE_SCTP |
56 | 56 |
W_OPT_NSCTP(sctp_autoclose); |
57 | 57 |
W_OPT_NSCTP(sctp_send_ttl); |
58 |
+ W_OPT_NSCTP(sctp_send_retries); |
|
59 |
+ if (sctp_options.sctp_send_retries>MAX_SCTP_SEND_RETRIES) { |
|
60 |
+ WARN("sctp: sctp_send_retries too high (%d), setting it to %d\n", |
|
61 |
+ sctp_option.sctp_send_retries, MAX_SCTP_SEND_RETRIES); |
|
62 |
+ sctp_options.sctp_send_retries=MAX_SCTP_SEND_RETRIES; |
|
63 |
+ } |
|
58 | 64 |
#endif |
59 | 65 |
} |
60 | 66 |
|
... | ... |
@@ -29,6 +29,8 @@ |
29 | 29 |
|
30 | 30 |
#define DEFAULT_SCTP_AUTOCLOSE 180 /* seconds */ |
31 | 31 |
#define DEFAULT_SCTP_SEND_TTL 32000 /* in ms (32s) */ |
32 |
+#define DEFAULT_SCTP_SEND_RETRIES 0 |
|
33 |
+#define MAX_SCTP_SEND_RETRIES 9 |
|
32 | 34 |
|
33 | 35 |
|
34 | 36 |
struct sctp_cfg_options{ |
... | ... |
@@ -36,6 +38,7 @@ struct sctp_cfg_options{ |
36 | 36 |
int sctp_so_sndbuf; |
37 | 37 |
unsigned int sctp_autoclose; /* in seconds */ |
38 | 38 |
unsigned int sctp_send_ttl; /* in milliseconds */ |
39 |
+ unsigned int sctp_send_retries; |
|
39 | 40 |
}; |
40 | 41 |
|
41 | 42 |
extern struct sctp_cfg_options sctp_options; |
... | ... |
@@ -508,6 +508,12 @@ error: |
508 | 508 |
#endif /* USE_SCTP_OO */ |
509 | 509 |
|
510 | 510 |
|
511 |
+ |
|
512 |
+static int sctp_msg_send_raw(struct dest_info* dst, char* buf, unsigned len, |
|
513 |
+ struct sctp_sndrcvinfo* sndrcv_info); |
|
514 |
+ |
|
515 |
+ |
|
516 |
+ |
|
511 | 517 |
/* debugging: return a string name for SCTP_ASSOC_CHANGE state */ |
512 | 518 |
static char* sctp_assoc_change_state2s(short int state) |
513 | 519 |
{ |
... | ... |
@@ -573,6 +579,67 @@ static char* sctp_paddr_change_state2s(unsigned int state) |
573 | 573 |
|
574 | 574 |
|
575 | 575 |
|
576 |
+/* handle SCTP_SEND_FAILED notifications: if packet marked for retries |
|
577 |
+ * retry the send (with 0 associd) |
|
578 |
+ * returns 0 on success, -1 on failure |
|
579 |
+ */ |
|
580 |
+static int sctp_handle_send_failed(struct socket_info* si, |
|
581 |
+ union sockaddr_union* su, |
|
582 |
+ char* buf, unsigned len) |
|
583 |
+{ |
|
584 |
+ union sctp_notification* snp; |
|
585 |
+ struct sctp_sndrcvinfo sinfo; |
|
586 |
+ struct dest_info dst; |
|
587 |
+ char* data; |
|
588 |
+ unsigned data_len; |
|
589 |
+ int retries; |
|
590 |
+ int ret; |
|
591 |
+ |
|
592 |
+ ret=-1; |
|
593 |
+ snp=(union sctp_notification*) buf; |
|
594 |
+ retries=snp->sn_send_failed.ssf_info.sinfo_context; |
|
595 |
+ |
|
596 |
+ /* don't retry on explicit remote error |
|
597 |
+ * (unfortunately we can't be more picky than this, we get no |
|
598 |
+ * indication in the SEND_FAILED notification for other error |
|
599 |
+ * reasons (e.g. ABORT received, INIT timeout a.s.o) |
|
600 |
+ */ |
|
601 |
+ if (retries && (snp->sn_send_failed.ssf_error==0)) { |
|
602 |
+ DBG("sctp: RETRY-ing (%d)\n", retries); |
|
603 |
+ retries--; |
|
604 |
+ data=(char*)snp->sn_send_failed.ssf_data; |
|
605 |
+ data_len=snp->sn_send_failed.ssf_length - |
|
606 |
+ sizeof(struct sctp_send_failed); |
|
607 |
+ |
|
608 |
+ memset(&sinfo, 0, sizeof(sinfo)); |
|
609 |
+ sinfo.sinfo_flags=SCTP_UNORDERED; |
|
610 |
+#ifdef HAVE_SCTP_SNDRCVINFO_PR_POLICY |
|
611 |
+ if (sctp_options.sctp_send_ttl){ |
|
612 |
+ sinfo.sinfo_pr_policy=SCTP_PR_SCTP_TTL; |
|
613 |
+ sinfo.sinfo_pr_value=sctp_options.sctp_send_ttl; |
|
614 |
+ }else |
|
615 |
+ sinfo.info_pr_policy=SCTP_PR_SCTP_NONE; |
|
616 |
+#else |
|
617 |
+ sinfo.sinfo_timetolive=sctp_options.sctp_send_ttl; |
|
618 |
+#endif |
|
619 |
+ sinfo.sinfo_context=retries; |
|
620 |
+ |
|
621 |
+ dst.to=*su; |
|
622 |
+ dst.send_sock=si; |
|
623 |
+ dst.id=0; |
|
624 |
+ dst.proto=PROTO_SCTP; |
|
625 |
+#ifdef USE_COMP |
|
626 |
+ dst.comp=COMP_NONE; |
|
627 |
+#endif |
|
628 |
+ |
|
629 |
+ ret=sctp_msg_send_raw(&dst, data, data_len, &sinfo); |
|
630 |
+ } |
|
631 |
+ |
|
632 |
+ return (ret>0)?0:ret; |
|
633 |
+} |
|
634 |
+ |
|
635 |
+ |
|
636 |
+ |
|
576 | 637 |
static int sctp_handle_notification(struct socket_info* si, |
577 | 638 |
union sockaddr_union* su, |
578 | 639 |
char* buf, unsigned len) |
... | ... |
@@ -623,6 +690,7 @@ static int sctp_handle_notification(struct socket_info* si, |
623 | 623 |
si->port_no, snp->sn_send_failed.ssf_error, |
624 | 624 |
snp->sn_send_failed.ssf_assoc_id, |
625 | 625 |
snp->sn_send_failed.ssf_flags); |
626 |
+ sctp_handle_send_failed(si, su, buf, len); |
|
626 | 627 |
break; |
627 | 628 |
case SCTP_PEER_ADDR_CHANGE: |
628 | 629 |
ERR_LEN_TOO_SMALL(len, sizeof(struct sctp_paddr_change), si, su, |
... | ... |
@@ -817,16 +885,25 @@ error: |
817 | 817 |
} |
818 | 818 |
|
819 | 819 |
|
820 |
-/* send buf:len over udp to dst (uses only the to and send_sock dst members) |
|
820 |
+/* send buf:len over udp to dst using sndrcv_info (uses only the to and |
|
821 |
+ * send_sock members from dst) |
|
821 | 822 |
* returns the numbers of bytes sent on success (>=0) and -1 on error |
822 | 823 |
*/ |
823 |
-int sctp_msg_send(struct dest_info* dst, char* buf, unsigned len) |
|
824 |
+static int sctp_msg_send_raw(struct dest_info* dst, char* buf, unsigned len, |
|
825 |
+ struct sctp_sndrcvinfo* sndrcv_info) |
|
824 | 826 |
{ |
825 | 827 |
int n; |
826 | 828 |
int tolen; |
827 | 829 |
struct ip_addr ip; /* used only on error, for debugging */ |
828 | 830 |
struct msghdr msg; |
829 | 831 |
struct iovec iov[1]; |
832 |
+ struct sctp_sndrcvinfo* sinfo; |
|
833 |
+ struct cmsghdr* cmsg; |
|
834 |
+ /* make sure msg_control will point to properly aligned data */ |
|
835 |
+ union { |
|
836 |
+ struct cmsghdr cm; |
|
837 |
+ char cbuf[CMSG_SPACE(sizeof(*sinfo))]; |
|
838 |
+ }ctrl_un; |
|
830 | 839 |
|
831 | 840 |
tolen=sockaddru_len(dst->to); |
832 | 841 |
iov[0].iov_base=buf; |
... | ... |
@@ -835,9 +912,18 @@ int sctp_msg_send(struct dest_info* dst, char* buf, unsigned len) |
835 | 835 |
msg.msg_iovlen=1; |
836 | 836 |
msg.msg_name=&dst->to.s; |
837 | 837 |
msg.msg_namelen=tolen; |
838 |
- msg.msg_control=0; |
|
839 |
- msg.msg_controllen=0; |
|
840 |
- msg.msg_flags=SCTP_UNORDERED; |
|
838 |
+ msg.msg_flags=0; /* not used on send (use instead sinfo_flags) */ |
|
839 |
+ msg.msg_control=ctrl_un.cbuf; |
|
840 |
+ msg.msg_controllen=sizeof(ctrl_un.cbuf); |
|
841 |
+ cmsg=CMSG_FIRSTHDR(&msg); |
|
842 |
+ cmsg->cmsg_level=IPPROTO_SCTP; |
|
843 |
+ cmsg->cmsg_type=SCTP_SNDRCV; |
|
844 |
+ cmsg->cmsg_len=CMSG_LEN(sizeof(*sinfo)); |
|
845 |
+ sinfo=(struct sctp_sndrcvinfo*)CMSG_DATA(cmsg); |
|
846 |
+ *sinfo=*sndrcv_info; |
|
847 |
+ /* some systems need msg_controllen set to the actual size and not |
|
848 |
+ * something bigger (e.g. openbsd) */ |
|
849 |
+ msg.msg_controllen=cmsg->cmsg_len; |
|
841 | 850 |
again: |
842 | 851 |
n=sendmsg(dst->send_sock->socket, &msg, MSG_DONTWAIT); |
843 | 852 |
#if 0 |
... | ... |
@@ -859,7 +945,6 @@ again: |
859 | 859 |
}else if (errno==EAGAIN || errno==EWOULDBLOCK){ |
860 | 860 |
LOG(L_ERR, "ERROR: sctp_msg_send: failed to send, send buffers" |
861 | 861 |
" full\n"); |
862 |
- /* TODO: fix blocking writes */ |
|
863 | 862 |
} |
864 | 863 |
} |
865 | 864 |
return n; |
... | ... |
@@ -867,6 +952,32 @@ again: |
867 | 867 |
|
868 | 868 |
|
869 | 869 |
|
870 |
+/* wrapper around sctp_msg_send_raw(): |
|
871 |
+ * send buf:len over udp to dst (uses only the to and send_sock members |
|
872 |
+ * from dst) |
|
873 |
+ * returns the numbers of bytes sent on success (>=0) and -1 on error |
|
874 |
+ */ |
|
875 |
+int sctp_msg_send(struct dest_info* dst, char* buf, unsigned len) |
|
876 |
+{ |
|
877 |
+ struct sctp_sndrcvinfo sinfo; |
|
878 |
+ |
|
879 |
+ memset(&sinfo, 0, sizeof(sinfo)); |
|
880 |
+ sinfo.sinfo_flags=SCTP_UNORDERED; |
|
881 |
+#ifdef HAVE_SCTP_SNDRCVINFO_PR_POLICY |
|
882 |
+ if (sctp_options.sctp_send_ttl){ |
|
883 |
+ sinfo.sinfo_pr_policy=SCTP_PR_SCTP_TTL; |
|
884 |
+ sinfo.sinfo_pr_value=sctp_options.sctp_send_ttl; |
|
885 |
+ }else |
|
886 |
+ sinfo->sinfo_pr_policy=SCTP_PR_SCTP_NONE; |
|
887 |
+#else |
|
888 |
+ sinfo.sinfo_timetolive=sctp_options.sctp_send_ttl; |
|
889 |
+#endif |
|
890 |
+ sinfo.sinfo_context=sctp_options.sctp_send_retries; |
|
891 |
+ return sctp_msg_send_raw(dst, buf, len, &sinfo); |
|
892 |
+} |
|
893 |
+ |
|
894 |
+ |
|
895 |
+ |
|
870 | 896 |
void destroy_sctp() |
871 | 897 |
{ |
872 | 898 |
} |