Browse code

sctp: max_assocs options

Added a new option controlling the maximum number of open
associations allowed. When exceeded new associations will be
immediately closed (using ABORT).
If connection tracking is used (default) trying to open new
associations will gracefully fail before actually opening them. If
no connection tracking is used, the associations will first be
opened and then immediately closed. In this case the packet
triggering the active open will be sent (as part of the initial
4-way handshake), before the association is closed.

Andrei Pelinescu-Onciul authored on 26/06/2009 17:56:44
Showing 3 changed files
... ...
@@ -83,6 +83,9 @@ static cfg_def_t sctp_cfg_def[] = {
83 83
 	{ "assoc_reuse", CFG_VAR_INT| CFG_ATOMIC, 0, 1, set_assoc_reuse, 0,
84 84
 		"connection/association reuse (for now used only for replies)"
85 85
 		", depends on assoc_tracking being set"},
86
+	{ "max_assocs", CFG_VAR_INT| CFG_ATOMIC, 0, 0, 0, 0,
87
+		"maximum allowed open associations (-1 = disable, "
88
+			"as many as allowed by the OS)"},
86 89
 	{ "srto_initial", CFG_VAR_INT| CFG_ATOMIC, 0, 1<<30, set_srto_initial, 0,
87 90
 		"initial value of the retr. timeout, used in RTO calculations,"
88 91
 			" in msecs" },
... ...
@@ -127,6 +130,7 @@ void init_sctp_options()
127 130
 	sctp_default_cfg.autoclose=DEFAULT_SCTP_AUTOCLOSE; /* in seconds */
128 131
 	sctp_default_cfg.send_ttl=DEFAULT_SCTP_SEND_TTL;   /* in milliseconds */
129 132
 	sctp_default_cfg.send_retries=DEFAULT_SCTP_SEND_RETRIES;
133
+	sctp_default_cfg.max_assocs=-1; /* as much as possible by default */
130 134
 #ifdef SCTP_CONN_REUSE
131 135
 	sctp_default_cfg.assoc_tracking=1; /* on by default */
132 136
 	sctp_default_cfg.assoc_reuse=1; /* on by default */
... ...
@@ -156,6 +160,7 @@ void sctp_options_check()
156 160
 	W_OPT_NSCTP(send_retries);
157 161
 	W_OPT_NSCTP(assoc_tracking);
158 162
 	W_OPT_NSCTP(assoc_reuse);
163
+	W_OPT_NSCTP(max_assocs);
159 164
 #else /* USE_SCTP */
160 165
 	if (sctp_default_cfg.send_retries>MAX_SCTP_SEND_RETRIES) {
161 166
 		WARN("sctp: sctp_send_retries too high (%d), setting it to %d\n",
... ...
@@ -48,6 +48,7 @@ struct cfg_group_sctp{
48 48
 	int assoc_tracking; /* track associations */
49 49
 	int assoc_reuse; /* reuse the request connection for sending the reply,
50 50
 					    depends on assoc_tracking */
51
+	int max_assocs; /* maximum associations, -1 means disabled */
51 52
 	unsigned int srto_initial; /** initial retr. timeout */
52 53
 	unsigned int srto_max;     /** max retr. timeout */
53 54
 	unsigned int srto_min;     /** min retr. timeout */
... ...
@@ -66,6 +66,8 @@
66 66
 static atomic_t* sctp_conn_no;
67 67
 
68 68
 
69
+#define ABORT_REASON_MAX_ASSOCS \
70
+	"Maximum configured number of open associations exceeded"
69 71
 
70 72
 /* check if the underlying OS supports sctp
71 73
    returns 0 if yes, -1 on error */
... ...
@@ -1927,8 +1929,13 @@ void destroy_sctp()
1927 1929
 
1928 1930
 
1929 1931
 
1930
-static int sctp_msg_send_raw(struct dest_info* dst, char* buf, unsigned len,
1932
+static int sctp_msg_send_ext(struct dest_info* dst, char* buf, unsigned len,
1931 1933
 						struct sctp_sndrcvinfo* sndrcv_info);
1934
+#define SCTP_SEND_FIRST_ASSOCID 1  /* sctp_raw_send flag */
1935
+static int sctp_raw_send(int socket, char* buf, unsigned len,
1936
+						union sockaddr_union* to,
1937
+						struct sctp_sndrcvinfo* sndrcv_info,
1938
+						int flags);
1932 1939
 
1933 1940
 
1934 1941
 
... ...
@@ -2055,7 +2062,7 @@ static int sctp_handle_send_failed(struct socket_info* si,
2055 2062
 		dst.comp=COMP_NONE;
2056 2063
 #endif
2057 2064
 		
2058
-		ret=sctp_msg_send_raw(&dst, data, data_len, &sinfo);
2065
+		ret=sctp_msg_send_ext(&dst, data, data_len, &sinfo);
2059 2066
 	}
2060 2067
 #ifdef USE_DST_BLACKLIST
2061 2068
 	 else if (cfg_get(core, core_cfg, use_dst_blacklist) &&
... ...
@@ -2083,10 +2090,17 @@ static int sctp_handle_send_failed(struct socket_info* si,
2083 2090
  */
2084 2091
 static int sctp_handle_assoc_change(struct socket_info* si,
2085 2092
 									union sockaddr_union* su,
2086
-									int state,
2087
-									int assoc_id)
2093
+									union sctp_notification* snp
2094
+									)
2088 2095
 {
2089 2096
 	int ret;
2097
+	int state;
2098
+	int assoc_id;
2099
+	struct sctp_sndrcvinfo sinfo;
2100
+	struct ip_addr ip; /* used only on error, for debugging */
2101
+	
2102
+	state=snp->sn_assoc_change.sac_state;
2103
+	assoc_id=snp->sn_assoc_change.sac_assoc_id;
2090 2104
 	
2091 2105
 	ret=-1;
2092 2106
 	switch(state){
... ...
@@ -2113,6 +2127,23 @@ again:
2113 2127
 			}
2114 2128
 #endif
2115 2129
 #endif /* SCTP_CONN_REUSE */
2130
+			if (unlikely((unsigned)atomic_get(sctp_conn_no) >
2131
+							(unsigned)cfg_get(sctp, sctp_cfg, max_assocs))){
2132
+				/* maximum assoc exceeded => we'll have to immediately 
2133
+				   close it */
2134
+				memset(&sinfo, 0, sizeof(sinfo));
2135
+				sinfo.sinfo_flags=SCTP_UNORDERED | SCTP_ABORT;
2136
+				sinfo.sinfo_assoc_id=assoc_id;
2137
+				ret=sctp_raw_send(si->socket, ABORT_REASON_MAX_ASSOCS,
2138
+											sizeof(ABORT_REASON_MAX_ASSOCS)-1,
2139
+											su, &sinfo, 0);
2140
+				if (ret<0){
2141
+					su2ip_addr(&ip, su);
2142
+					WARN("failed to ABORT new sctp association %d (%s:%d):"
2143
+							" %s (%d)\n", assoc_id, ip_addr2a(&ip),
2144
+							su_getport(su), strerror(errno), errno);
2145
+				}
2146
+			}
2116 2147
 			break;
2117 2148
 		case SCTP_COMM_LOST:
2118 2149
 			SCTP_STATS_COMM_LOST();
... ...
@@ -2259,8 +2290,7 @@ static int sctp_handle_notification(struct socket_info* si,
2259 2290
 					snp->sn_assoc_change.sac_outbound_streams,
2260 2291
 					snp->sn_assoc_change.sac_inbound_streams
2261 2292
 					);
2262
-			sctp_handle_assoc_change(si, su, snp->sn_assoc_change.sac_state,
2263
-										snp->sn_assoc_change.sac_assoc_id);
2293
+			sctp_handle_assoc_change(si, su, snp);
2264 2294
 			break;
2265 2295
 #ifdef SCTP_ADAPTION_INDICATION
2266 2296
 		case SCTP_ADAPTION_INDICATION:
... ...
@@ -2442,11 +2472,155 @@ error:
2442 2472
 
2443 2473
 
2444 2474
 
2475
+/** low level sctp non-blocking send.
2476
+ * @param socket - sctp socket to send on.
2477
+ * @param buf   - data.
2478
+ * @param len   - lenght of the data.
2479
+ * @param to    - destination in ser sockaddr_union format.
2480
+ * @param sndrcv_info - sctp_sndrcvinfo structure pointer, pre-filled.
2481
+ * @param flags - can have one of the following values (or'ed):
2482
+ *                SCTP_SEND_FIRST_ASSOCID - try to send first to assoc_id
2483
+ *                and only if that fails use "to".
2484
+ * @return the numbers of bytes sent on success (>=0) and -1 on error.
2485
+ * On error errno is set too.
2486
+ */
2487
+static int sctp_raw_send(int socket, char* buf, unsigned len,
2488
+						union sockaddr_union* to,
2489
+						struct sctp_sndrcvinfo* sndrcv_info,
2490
+						int flags)
2491
+{
2492
+	int n;
2493
+	int tolen;
2494
+	int try_assoc_id;
2495
+#if 0
2496
+	struct ip_addr ip; /* used only on error, for debugging */
2497
+#endif
2498
+	struct msghdr msg;
2499
+	struct iovec iov[1];
2500
+	struct sctp_sndrcvinfo* sinfo;
2501
+	struct cmsghdr* cmsg;
2502
+	/* make sure msg_control will point to properly aligned data */
2503
+	union {
2504
+		struct cmsghdr cm;
2505
+		char cbuf[CMSG_SPACE(sizeof(*sinfo))];
2506
+	}ctrl_un;
2507
+	
2508
+	iov[0].iov_base=buf;
2509
+	iov[0].iov_len=len;
2510
+	msg.msg_iov=iov;
2511
+	msg.msg_iovlen=1;
2512
+	msg.msg_flags=0; /* not used on send (use instead sinfo_flags) */
2513
+	msg.msg_control=ctrl_un.cbuf;
2514
+	msg.msg_controllen=sizeof(ctrl_un.cbuf);
2515
+	cmsg=CMSG_FIRSTHDR(&msg);
2516
+	cmsg->cmsg_level=IPPROTO_SCTP;
2517
+	cmsg->cmsg_type=SCTP_SNDRCV;
2518
+	cmsg->cmsg_len=CMSG_LEN(sizeof(*sinfo));
2519
+	sinfo=(struct sctp_sndrcvinfo*)CMSG_DATA(cmsg);
2520
+	*sinfo=*sndrcv_info;
2521
+	/* some systems need msg_controllen set to the actual size and not
2522
+	 * something bigger (e.g. openbsd) */
2523
+	msg.msg_controllen=cmsg->cmsg_len;
2524
+	try_assoc_id= ((flags & SCTP_SEND_FIRST_ASSOCID) && sinfo->sinfo_assoc_id);
2525
+	/* if assoc_id is set it means we want to send on association assoc_id
2526
+	   and only if it's not opened any longer use the addresses */
2527
+	if (try_assoc_id){
2528
+		/* on linux msg->name has priority over assoc_id. To try first assoc_id
2529
+		 * and then "to", one has to call first sendmsg() with msg->name==0 and
2530
+		 * sinfo->assoc_id set. If it returns EPIPE => association is no longer
2531
+		 * open => call again sendmsg() this time with msg->name!=0.
2532
+		 * on freebsd assoc_id has priority over msg->name and moreover the
2533
+		 * send falls back automatically to the address if the assoc_id is
2534
+		 * closed, so a single call to sendmsg(msg->name, sinfo->assoc_id ) is
2535
+		 * enough.  If one tries calling with msg->name==0 and the association
2536
+		 * is no longer open send will return ENOENT.
2537
+		 * on solaris it seems one must always use a dst address (assoc_id
2538
+		 * will be ignored).
2539
+		 */
2540
+#ifdef __OS_linux
2541
+		msg.msg_name=0;
2542
+		msg.msg_namelen=0;
2543
+#elif defined __OS_freebsd
2544
+		tolen=sockaddru_len(*to);
2545
+		msg.msg_name=&to->s;
2546
+		msg.msg_namelen=tolen;
2547
+#else /* __OS_* */
2548
+		/* fallback for solaris and others, sent back to
2549
+		  the address recorded (not exactly what we want, but there's
2550
+		  no way to fallback to "to") */
2551
+		tolen=sockaddru_len(*to);
2552
+		msg.msg_name=&to->s;
2553
+		msg.msg_namelen=tolen;
2554
+#endif /* __OS_* */
2555
+	}else{
2556
+		tolen=sockaddru_len(*to);
2557
+		msg.msg_name=&to->s;
2558
+		msg.msg_namelen=tolen;
2559
+	}
2560
+	
2561
+again:
2562
+	n=sendmsg(socket, &msg, MSG_DONTWAIT);
2563
+	if (n==-1){
2564
+#ifdef __OS_linux
2565
+		if ((errno==EPIPE) && try_assoc_id){
2566
+			/* try again, this time with null assoc_id and non-null msg.name */
2567
+			DBG("sctp raw sendmsg: assoc already closed (EPIPE), retrying with"
2568
+					" assoc_id=0\n");
2569
+			tolen=sockaddru_len(*to);
2570
+			msg.msg_name=&to->s;
2571
+			msg.msg_namelen=tolen;
2572
+			sinfo->sinfo_assoc_id=0;
2573
+			try_assoc_id=0;
2574
+			goto again;
2575
+		}
2576
+#elif defined __OS_freebsd
2577
+		if ((errno==ENOENT)){
2578
+			/* it didn't work, no retrying */
2579
+			WARN("unexpected sendmsg() failure (ENOENT),"
2580
+					" assoc_id %d\n", sinfo->sinfo_assoc_id);
2581
+		}
2582
+#else /* __OS_* */
2583
+		if ((errno==ENOENT || errno==EPIPE) && try_assoc_id){
2584
+			/* in case the sctp stack prioritises assoc_id over msg->name,
2585
+			   try again with 0 assoc_id and msg->name set to "to" */
2586
+			WARN("unexpected ENOENT or EPIPE (assoc_id %d),"
2587
+					"trying automatic recovery... (please report along with"
2588
+					"your OS version)\n", sinfo->sinfo_assoc_id);
2589
+			tolen=sockaddru_len(*to);
2590
+			msg.msg_name=&to->s;
2591
+			msg.msg_namelen=tolen;
2592
+			sinfo->sinfo_assoc_id=0;
2593
+			try_assoc_id=0;
2594
+			goto again;
2595
+		}
2596
+#endif /* __OS_* */
2597
+#if 0
2598
+		if (errno==EINTR) goto again;
2599
+		su2ip_addr(&ip, to);
2600
+		LOG(L_ERR, "ERROR: sctp_raw_send: sendmsg(sock,%p,%d,0,%s:%d,...):"
2601
+				" %s(%d)\n", buf, len, ip_addr2a(&ip), su_getport(to),
2602
+				strerror(errno), errno);
2603
+		if (errno==EINVAL) {
2604
+			LOG(L_CRIT,"CRITICAL: invalid sendmsg parameters\n"
2605
+			"one possible reason is the server is bound to localhost and\n"
2606
+			"attempts to send to the net\n");
2607
+		}else if (errno==EAGAIN || errno==EWOULDBLOCK){
2608
+			SCTP_STATS_SENDQ_FULL();
2609
+			LOG(L_ERR, "ERROR: sctp_msg_send: failed to send, send buffers"
2610
+						" full\n");
2611
+		}
2612
+#endif
2613
+	}
2614
+	return n;
2615
+}
2616
+
2617
+
2618
+
2445 2619
 /* send buf:len over sctp to dst using sndrcv_info (uses send_sock,
2446 2620
  * to and id from dest_info)
2447 2621
  * returns the numbers of bytes sent on success (>=0) and -1 on error
2448 2622
  */
2449
-static int sctp_msg_send_raw(struct dest_info* dst, char* buf, unsigned len,
2623
+static int sctp_msg_send_ext(struct dest_info* dst, char* buf, unsigned len,
2450 2624
 						struct sctp_sndrcvinfo* sndrcv_info)
2451 2625
 {
2452 2626
 	int n;
... ...
@@ -2521,8 +2695,8 @@ static int sctp_msg_send_raw(struct dest_info* dst, char* buf, unsigned len,
2521 2695
 		/* fallback for solaris and others, sent back to
2522 2696
 		  the address recorded (not exactly what we want, but there's 
2523 2697
 		  no way to fallback to dst->to) */
2524
-		tolen=sockaddru_len(&to);
2525
-		msg.msg_name=&to.s;
2698
+		tolen=sockaddru_len(dst->to);
2699
+		msg.msg_name=&dst->to.s;
2526 2700
 		msg.msg_namelen=tolen;
2527 2701
 #endif /* __OS_* */
2528 2702
 	}else{
... ...
@@ -2533,6 +2707,12 @@ static int sctp_msg_send_raw(struct dest_info* dst, char* buf, unsigned len,
2533 2707
 												&tmp_assoc_id, 0);
2534 2708
 			DBG("sctp send: timeout updated ser id %d, sctp assoc_id %d\n",
2535 2709
 					tmp_id, tmp_assoc_id);
2710
+			if (tmp_id==0 /* not tracked/found */ &&
2711
+					(unsigned)atomic_get(sctp_conn_tracked) >=
2712
+						(unsigned)cfg_get(sctp, sctp_cfg, max_assocs)){
2713
+				ERR("maximum number of sctp associations exceeded\n");
2714
+				goto error;
2715
+			}
2536 2716
 		}
2537 2717
 #endif /* SCTP_ADDR_HASH */
2538 2718
 		tolen=sockaddru_len(dst->to);
... ...
@@ -2597,11 +2777,17 @@ again:
2597 2777
 		}
2598 2778
 	}
2599 2779
 	return n;
2780
+#ifdef SCTP_CONN_REUSE
2781
+#ifdef SCTP_ADDR_HASH
2782
+error:
2783
+	return -1;
2784
+#endif /* SCTP_ADDR_HASH */
2785
+#endif /* SCTP_CONN_REUSE */
2600 2786
 }
2601 2787
 
2602 2788
 
2603 2789
 
2604
-/* wrapper around sctp_msg_send_raw():
2790
+/* wrapper around sctp_msg_send_ext():
2605 2791
  * send buf:len over udp to dst (uses only the to, send_sock and id members
2606 2792
  * from dst)
2607 2793
  * returns the numbers of bytes sent on success (>=0) and -1 on error
... ...
@@ -2625,7 +2811,7 @@ int sctp_msg_send(struct dest_info* dst, char* buf, unsigned len)
2625 2811
 		sinfo.sinfo_timetolive=cfg_get(sctp, sctp_cfg, send_ttl);
2626 2812
 #endif
2627 2813
 	sinfo.sinfo_context=cfg_get(sctp, sctp_cfg, send_retries);
2628
-	return sctp_msg_send_raw(dst, buf, len, &sinfo);
2814
+	return sctp_msg_send_ext(dst, buf, len, &sinfo);
2629 2815
 }
2630 2816
 
2631 2817