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 127
 	sctp_default_cfg.autoclose=DEFAULT_SCTP_AUTOCLOSE; /* in seconds */
128 128
 	sctp_default_cfg.send_ttl=DEFAULT_SCTP_SEND_TTL;   /* in milliseconds */
129 129
 	sctp_default_cfg.send_retries=DEFAULT_SCTP_SEND_RETRIES;
130
+	sctp_default_cfg.max_assocs=-1; /* as much as possible by default */
130 131
 #ifdef SCTP_CONN_REUSE
131 132
 	sctp_default_cfg.assoc_tracking=1; /* on by default */
132 133
 	sctp_default_cfg.assoc_reuse=1; /* on by default */
... ...
@@ -156,6 +160,7 @@ void sctp_options_check()
156 156
 	W_OPT_NSCTP(send_retries);
157 157
 	W_OPT_NSCTP(assoc_tracking);
158 158
 	W_OPT_NSCTP(assoc_reuse);
159
+	W_OPT_NSCTP(max_assocs);
159 160
 #else /* USE_SCTP */
160 161
 	if (sctp_default_cfg.send_retries>MAX_SCTP_SEND_RETRIES) {
161 162
 		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 1927
 
1928 1928
 
1929 1929
 
1930
-static int sctp_msg_send_raw(struct dest_info* dst, char* buf, unsigned len,
1930
+static int sctp_msg_send_ext(struct dest_info* dst, char* buf, unsigned len,
1931 1931
 						struct sctp_sndrcvinfo* sndrcv_info);
1932
+#define SCTP_SEND_FIRST_ASSOCID 1  /* sctp_raw_send flag */
1933
+static int sctp_raw_send(int socket, char* buf, unsigned len,
1934
+						union sockaddr_union* to,
1935
+						struct sctp_sndrcvinfo* sndrcv_info,
1936
+						int flags);
1932 1937
 
1933 1938
 
1934 1939
 
... ...
@@ -2055,7 +2062,7 @@ static int sctp_handle_send_failed(struct socket_info* si,
2055 2055
 		dst.comp=COMP_NONE;
2056 2056
 #endif
2057 2057
 		
2058
-		ret=sctp_msg_send_raw(&dst, data, data_len, &sinfo);
2058
+		ret=sctp_msg_send_ext(&dst, data, data_len, &sinfo);
2059 2059
 	}
2060 2060
 #ifdef USE_DST_BLACKLIST
2061 2061
 	 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 2083
  */
2084 2084
 static int sctp_handle_assoc_change(struct socket_info* si,
2085 2085
 									union sockaddr_union* su,
2086
-									int state,
2087
-									int assoc_id)
2086
+									union sctp_notification* snp
2087
+									)
2088 2088
 {
2089 2089
 	int ret;
2090
+	int state;
2091
+	int assoc_id;
2092
+	struct sctp_sndrcvinfo sinfo;
2093
+	struct ip_addr ip; /* used only on error, for debugging */
2094
+	
2095
+	state=snp->sn_assoc_change.sac_state;
2096
+	assoc_id=snp->sn_assoc_change.sac_assoc_id;
2090 2097
 	
2091 2098
 	ret=-1;
2092 2099
 	switch(state){
... ...
@@ -2113,6 +2127,23 @@ again:
2113 2113
 			}
2114 2114
 #endif
2115 2115
 #endif /* SCTP_CONN_REUSE */
2116
+			if (unlikely((unsigned)atomic_get(sctp_conn_no) >
2117
+							(unsigned)cfg_get(sctp, sctp_cfg, max_assocs))){
2118
+				/* maximum assoc exceeded => we'll have to immediately 
2119
+				   close it */
2120
+				memset(&sinfo, 0, sizeof(sinfo));
2121
+				sinfo.sinfo_flags=SCTP_UNORDERED | SCTP_ABORT;
2122
+				sinfo.sinfo_assoc_id=assoc_id;
2123
+				ret=sctp_raw_send(si->socket, ABORT_REASON_MAX_ASSOCS,
2124
+											sizeof(ABORT_REASON_MAX_ASSOCS)-1,
2125
+											su, &sinfo, 0);
2126
+				if (ret<0){
2127
+					su2ip_addr(&ip, su);
2128
+					WARN("failed to ABORT new sctp association %d (%s:%d):"
2129
+							" %s (%d)\n", assoc_id, ip_addr2a(&ip),
2130
+							su_getport(su), strerror(errno), errno);
2131
+				}
2132
+			}
2116 2133
 			break;
2117 2134
 		case SCTP_COMM_LOST:
2118 2135
 			SCTP_STATS_COMM_LOST();
... ...
@@ -2259,8 +2290,7 @@ static int sctp_handle_notification(struct socket_info* si,
2259 2259
 					snp->sn_assoc_change.sac_outbound_streams,
2260 2260
 					snp->sn_assoc_change.sac_inbound_streams
2261 2261
 					);
2262
-			sctp_handle_assoc_change(si, su, snp->sn_assoc_change.sac_state,
2263
-										snp->sn_assoc_change.sac_assoc_id);
2262
+			sctp_handle_assoc_change(si, su, snp);
2264 2263
 			break;
2265 2264
 #ifdef SCTP_ADAPTION_INDICATION
2266 2265
 		case SCTP_ADAPTION_INDICATION:
... ...
@@ -2442,11 +2472,155 @@ error:
2442 2442
 
2443 2443
 
2444 2444
 
2445
+/** low level sctp non-blocking send.
2446
+ * @param socket - sctp socket to send on.
2447
+ * @param buf   - data.
2448
+ * @param len   - lenght of the data.
2449
+ * @param to    - destination in ser sockaddr_union format.
2450
+ * @param sndrcv_info - sctp_sndrcvinfo structure pointer, pre-filled.
2451
+ * @param flags - can have one of the following values (or'ed):
2452
+ *                SCTP_SEND_FIRST_ASSOCID - try to send first to assoc_id
2453
+ *                and only if that fails use "to".
2454
+ * @return the numbers of bytes sent on success (>=0) and -1 on error.
2455
+ * On error errno is set too.
2456
+ */
2457
+static int sctp_raw_send(int socket, char* buf, unsigned len,
2458
+						union sockaddr_union* to,
2459
+						struct sctp_sndrcvinfo* sndrcv_info,
2460
+						int flags)
2461
+{
2462
+	int n;
2463
+	int tolen;
2464
+	int try_assoc_id;
2465
+#if 0
2466
+	struct ip_addr ip; /* used only on error, for debugging */
2467
+#endif
2468
+	struct msghdr msg;
2469
+	struct iovec iov[1];
2470
+	struct sctp_sndrcvinfo* sinfo;
2471
+	struct cmsghdr* cmsg;
2472
+	/* make sure msg_control will point to properly aligned data */
2473
+	union {
2474
+		struct cmsghdr cm;
2475
+		char cbuf[CMSG_SPACE(sizeof(*sinfo))];
2476
+	}ctrl_un;
2477
+	
2478
+	iov[0].iov_base=buf;
2479
+	iov[0].iov_len=len;
2480
+	msg.msg_iov=iov;
2481
+	msg.msg_iovlen=1;
2482
+	msg.msg_flags=0; /* not used on send (use instead sinfo_flags) */
2483
+	msg.msg_control=ctrl_un.cbuf;
2484
+	msg.msg_controllen=sizeof(ctrl_un.cbuf);
2485
+	cmsg=CMSG_FIRSTHDR(&msg);
2486
+	cmsg->cmsg_level=IPPROTO_SCTP;
2487
+	cmsg->cmsg_type=SCTP_SNDRCV;
2488
+	cmsg->cmsg_len=CMSG_LEN(sizeof(*sinfo));
2489
+	sinfo=(struct sctp_sndrcvinfo*)CMSG_DATA(cmsg);
2490
+	*sinfo=*sndrcv_info;
2491
+	/* some systems need msg_controllen set to the actual size and not
2492
+	 * something bigger (e.g. openbsd) */
2493
+	msg.msg_controllen=cmsg->cmsg_len;
2494
+	try_assoc_id= ((flags & SCTP_SEND_FIRST_ASSOCID) && sinfo->sinfo_assoc_id);
2495
+	/* if assoc_id is set it means we want to send on association assoc_id
2496
+	   and only if it's not opened any longer use the addresses */
2497
+	if (try_assoc_id){
2498
+		/* on linux msg->name has priority over assoc_id. To try first assoc_id
2499
+		 * and then "to", one has to call first sendmsg() with msg->name==0 and
2500
+		 * sinfo->assoc_id set. If it returns EPIPE => association is no longer
2501
+		 * open => call again sendmsg() this time with msg->name!=0.
2502
+		 * on freebsd assoc_id has priority over msg->name and moreover the
2503
+		 * send falls back automatically to the address if the assoc_id is
2504
+		 * closed, so a single call to sendmsg(msg->name, sinfo->assoc_id ) is
2505
+		 * enough.  If one tries calling with msg->name==0 and the association
2506
+		 * is no longer open send will return ENOENT.
2507
+		 * on solaris it seems one must always use a dst address (assoc_id
2508
+		 * will be ignored).
2509
+		 */
2510
+#ifdef __OS_linux
2511
+		msg.msg_name=0;
2512
+		msg.msg_namelen=0;
2513
+#elif defined __OS_freebsd
2514
+		tolen=sockaddru_len(*to);
2515
+		msg.msg_name=&to->s;
2516
+		msg.msg_namelen=tolen;
2517
+#else /* __OS_* */
2518
+		/* fallback for solaris and others, sent back to
2519
+		  the address recorded (not exactly what we want, but there's
2520
+		  no way to fallback to "to") */
2521
+		tolen=sockaddru_len(*to);
2522
+		msg.msg_name=&to->s;
2523
+		msg.msg_namelen=tolen;
2524
+#endif /* __OS_* */
2525
+	}else{
2526
+		tolen=sockaddru_len(*to);
2527
+		msg.msg_name=&to->s;
2528
+		msg.msg_namelen=tolen;
2529
+	}
2530
+	
2531
+again:
2532
+	n=sendmsg(socket, &msg, MSG_DONTWAIT);
2533
+	if (n==-1){
2534
+#ifdef __OS_linux
2535
+		if ((errno==EPIPE) && try_assoc_id){
2536
+			/* try again, this time with null assoc_id and non-null msg.name */
2537
+			DBG("sctp raw sendmsg: assoc already closed (EPIPE), retrying with"
2538
+					" assoc_id=0\n");
2539
+			tolen=sockaddru_len(*to);
2540
+			msg.msg_name=&to->s;
2541
+			msg.msg_namelen=tolen;
2542
+			sinfo->sinfo_assoc_id=0;
2543
+			try_assoc_id=0;
2544
+			goto again;
2545
+		}
2546
+#elif defined __OS_freebsd
2547
+		if ((errno==ENOENT)){
2548
+			/* it didn't work, no retrying */
2549
+			WARN("unexpected sendmsg() failure (ENOENT),"
2550
+					" assoc_id %d\n", sinfo->sinfo_assoc_id);
2551
+		}
2552
+#else /* __OS_* */
2553
+		if ((errno==ENOENT || errno==EPIPE) && try_assoc_id){
2554
+			/* in case the sctp stack prioritises assoc_id over msg->name,
2555
+			   try again with 0 assoc_id and msg->name set to "to" */
2556
+			WARN("unexpected ENOENT or EPIPE (assoc_id %d),"
2557
+					"trying automatic recovery... (please report along with"
2558
+					"your OS version)\n", sinfo->sinfo_assoc_id);
2559
+			tolen=sockaddru_len(*to);
2560
+			msg.msg_name=&to->s;
2561
+			msg.msg_namelen=tolen;
2562
+			sinfo->sinfo_assoc_id=0;
2563
+			try_assoc_id=0;
2564
+			goto again;
2565
+		}
2566
+#endif /* __OS_* */
2567
+#if 0
2568
+		if (errno==EINTR) goto again;
2569
+		su2ip_addr(&ip, to);
2570
+		LOG(L_ERR, "ERROR: sctp_raw_send: sendmsg(sock,%p,%d,0,%s:%d,...):"
2571
+				" %s(%d)\n", buf, len, ip_addr2a(&ip), su_getport(to),
2572
+				strerror(errno), errno);
2573
+		if (errno==EINVAL) {
2574
+			LOG(L_CRIT,"CRITICAL: invalid sendmsg parameters\n"
2575
+			"one possible reason is the server is bound to localhost and\n"
2576
+			"attempts to send to the net\n");
2577
+		}else if (errno==EAGAIN || errno==EWOULDBLOCK){
2578
+			SCTP_STATS_SENDQ_FULL();
2579
+			LOG(L_ERR, "ERROR: sctp_msg_send: failed to send, send buffers"
2580
+						" full\n");
2581
+		}
2582
+#endif
2583
+	}
2584
+	return n;
2585
+}
2586
+
2587
+
2588
+
2445 2589
 /* send buf:len over sctp to dst using sndrcv_info (uses send_sock,
2446 2590
  * to and id from dest_info)
2447 2591
  * returns the numbers of bytes sent on success (>=0) and -1 on error
2448 2592
  */
2449
-static int sctp_msg_send_raw(struct dest_info* dst, char* buf, unsigned len,
2593
+static int sctp_msg_send_ext(struct dest_info* dst, char* buf, unsigned len,
2450 2594
 						struct sctp_sndrcvinfo* sndrcv_info)
2451 2595
 {
2452 2596
 	int n;
... ...
@@ -2521,8 +2695,8 @@ static int sctp_msg_send_raw(struct dest_info* dst, char* buf, unsigned len,
2521 2521
 		/* fallback for solaris and others, sent back to
2522 2522
 		  the address recorded (not exactly what we want, but there's 
2523 2523
 		  no way to fallback to dst->to) */
2524
-		tolen=sockaddru_len(&to);
2525
-		msg.msg_name=&to.s;
2524
+		tolen=sockaddru_len(dst->to);
2525
+		msg.msg_name=&dst->to.s;
2526 2526
 		msg.msg_namelen=tolen;
2527 2527
 #endif /* __OS_* */
2528 2528
 	}else{
... ...
@@ -2533,6 +2707,12 @@ static int sctp_msg_send_raw(struct dest_info* dst, char* buf, unsigned len,
2533 2533
 												&tmp_assoc_id, 0);
2534 2534
 			DBG("sctp send: timeout updated ser id %d, sctp assoc_id %d\n",
2535 2535
 					tmp_id, tmp_assoc_id);
2536
+			if (tmp_id==0 /* not tracked/found */ &&
2537
+					(unsigned)atomic_get(sctp_conn_tracked) >=
2538
+						(unsigned)cfg_get(sctp, sctp_cfg, max_assocs)){
2539
+				ERR("maximum number of sctp associations exceeded\n");
2540
+				goto error;
2541
+			}
2536 2542
 		}
2537 2543
 #endif /* SCTP_ADDR_HASH */
2538 2544
 		tolen=sockaddru_len(dst->to);
... ...
@@ -2597,11 +2777,17 @@ again:
2597 2597
 		}
2598 2598
 	}
2599 2599
 	return n;
2600
+#ifdef SCTP_CONN_REUSE
2601
+#ifdef SCTP_ADDR_HASH
2602
+error:
2603
+	return -1;
2604
+#endif /* SCTP_ADDR_HASH */
2605
+#endif /* SCTP_CONN_REUSE */
2600 2606
 }
2601 2607
 
2602 2608
 
2603 2609
 
2604
-/* wrapper around sctp_msg_send_raw():
2610
+/* wrapper around sctp_msg_send_ext():
2605 2611
  * send buf:len over udp to dst (uses only the to, send_sock and id members
2606 2612
  * from dst)
2607 2613
  * 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 2625
 		sinfo.sinfo_timetolive=cfg_get(sctp, sctp_cfg, send_ttl);
2626 2626
 #endif
2627 2627
 	sinfo.sinfo_context=cfg_get(sctp, sctp_cfg, send_retries);
2628
-	return sctp_msg_send_raw(dst, buf, len, &sinfo);
2628
+	return sctp_msg_send_ext(dst, buf, len, &sinfo);
2629 2629
 }
2630 2630
 
2631 2631