Browse code

sctp: send retries option and unordered fix

- 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

Andrei Pelinescu-Onciul authored on 07/11/2008 14:53:04
Showing 3 changed files
... ...
@@ -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
 }