Merge branch 'net-next' of git://git.kernel.org/pub/scm/linux/kernel/git/vxy/lksctp-dev
diff --git a/include/linux/sctp.h b/include/linux/sctp.h
index c2731bf..b464b9d 100644
--- a/include/linux/sctp.h
+++ b/include/linux/sctp.h
@@ -487,17 +487,17 @@
 	 *
 	 * Value          Cause Code
 	 * ---------      ----------------
-	 * 0x0100          Request to Delete Last Remaining IP Address.
-	 * 0x0101          Operation Refused Due to Resource Shortage.
-	 * 0x0102          Request to Delete Source IP Address.
-	 * 0x0103          Association Aborted due to illegal ASCONF-ACK
-	 * 0x0104          Request refused - no authorization.
+	 * 0x00A0          Request to Delete Last Remaining IP Address.
+	 * 0x00A1          Operation Refused Due to Resource Shortage.
+	 * 0x00A2          Request to Delete Source IP Address.
+	 * 0x00A3          Association Aborted due to illegal ASCONF-ACK
+	 * 0x00A4          Request refused - no authorization.
 	 */
-	SCTP_ERROR_DEL_LAST_IP	= cpu_to_be16(0x0100),
-	SCTP_ERROR_RSRC_LOW	= cpu_to_be16(0x0101),
-	SCTP_ERROR_DEL_SRC_IP	= cpu_to_be16(0x0102),
-	SCTP_ERROR_ASCONF_ACK   = cpu_to_be16(0x0103),
-	SCTP_ERROR_REQ_REFUSED	= cpu_to_be16(0x0104),
+	SCTP_ERROR_DEL_LAST_IP	= cpu_to_be16(0x00A0),
+	SCTP_ERROR_RSRC_LOW	= cpu_to_be16(0x00A1),
+	SCTP_ERROR_DEL_SRC_IP	= cpu_to_be16(0x00A2),
+	SCTP_ERROR_ASCONF_ACK   = cpu_to_be16(0x00A3),
+	SCTP_ERROR_REQ_REFUSED	= cpu_to_be16(0x00A4),
 
 	/* AUTH Section 4.  New Error Cause
 	 *
diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h
index 23f08fe..edfcacf 100644
--- a/include/net/sctp/structs.h
+++ b/include/net/sctp/structs.h
@@ -1939,10 +1939,8 @@
 void sctp_association_put(struct sctp_association *);
 void sctp_association_hold(struct sctp_association *);
 
-struct sctp_transport *sctp_assoc_choose_init_transport(
-	struct sctp_association *);
-struct sctp_transport *sctp_assoc_choose_shutdown_transport(
-	struct sctp_association *);
+struct sctp_transport *sctp_assoc_choose_alter_transport(
+	struct sctp_association *, struct sctp_transport *);
 void sctp_assoc_update_retran_path(struct sctp_association *);
 struct sctp_transport *sctp_assoc_lookup_paddr(const struct sctp_association *,
 					  const union sctp_addr *);
diff --git a/include/net/sctp/user.h b/include/net/sctp/user.h
index b259fc5..1580c04 100644
--- a/include/net/sctp/user.h
+++ b/include/net/sctp/user.h
@@ -147,6 +147,8 @@
 #define SCTP_GET_LOCAL_ADDRS	SCTP_GET_LOCAL_ADDRS
 	SCTP_SOCKOPT_CONNECTX, /* CONNECTX requests. */
 #define SCTP_SOCKOPT_CONNECTX	SCTP_SOCKOPT_CONNECTX
+	SCTP_SOCKOPT_CONNECTX3, /* CONNECTX requests. (new implementation) */
+#define SCTP_SOCKOPT_CONNECTX3	SCTP_SOCKOPT_CONNECTX3
 };
 
 /*
diff --git a/net/sctp/associola.c b/net/sctp/associola.c
index f4b2304..525864b 100644
--- a/net/sctp/associola.c
+++ b/net/sctp/associola.c
@@ -293,7 +293,8 @@
 	 * told otherwise.
 	 */
 	asoc->peer.ipv4_address = 1;
-	asoc->peer.ipv6_address = 1;
+	if (asoc->base.sk->sk_family == PF_INET6)
+		asoc->peer.ipv6_address = 1;
 	INIT_LIST_HEAD(&asoc->asocs);
 
 	asoc->autoclose = sp->autoclose;
@@ -566,6 +567,21 @@
 	if (asoc->init_last_sent_to == peer)
 		asoc->init_last_sent_to = NULL;
 
+	/* If we remove the transport an SHUTDOWN was last sent to, set it
+	 * to NULL. Combined with the update of the retran path above, this
+	 * will cause the next SHUTDOWN to be sent to the next available
+	 * transport, maintaining the cycle.
+	 */
+	if (asoc->shutdown_last_sent_to == peer)
+		asoc->shutdown_last_sent_to = NULL;
+
+	/* If we remove the transport an ASCONF was last sent to, set it to
+	 * NULL.
+	 */
+	if (asoc->addip_last_asconf &&
+	    asoc->addip_last_asconf->transport == peer)
+		asoc->addip_last_asconf->transport = NULL;
+
 	asoc->peer.transport_count--;
 
 	sctp_transport_free(peer);
@@ -1268,49 +1284,21 @@
 				 ntohs(t->ipaddr.v4.sin_port));
 }
 
-/* Choose the transport for sending a INIT packet.  */
-struct sctp_transport *sctp_assoc_choose_init_transport(
-	struct sctp_association *asoc)
+/* Choose the transport for sending retransmit packet.  */
+struct sctp_transport *sctp_assoc_choose_alter_transport(
+	struct sctp_association *asoc, struct sctp_transport *last_sent_to)
 {
-	struct sctp_transport *t;
-
-	/* Use the retran path. If the last INIT was sent over the
+	/* If this is the first time packet is sent, use the active path,
+	 * else use the retran path. If the last packet was sent over the
 	 * retran path, update the retran path and use it.
 	 */
-	if (!asoc->init_last_sent_to) {
-		t = asoc->peer.active_path;
-	} else {
-		if (asoc->init_last_sent_to == asoc->peer.retran_path)
-			sctp_assoc_update_retran_path(asoc);
-		t = asoc->peer.retran_path;
-	}
-
-	SCTP_DEBUG_PRINTK_IPADDR("sctp_assoc_update_retran_path:association"
-				 " %p addr: ",
-				 " port: %d\n",
-				 asoc,
-				 (&t->ipaddr),
-				 ntohs(t->ipaddr.v4.sin_port));
-
-	return t;
-}
-
-/* Choose the transport for sending a SHUTDOWN packet.  */
-struct sctp_transport *sctp_assoc_choose_shutdown_transport(
-	struct sctp_association *asoc)
-{
-	/* If this is the first time SHUTDOWN is sent, use the active path,
-	 * else use the retran path. If the last SHUTDOWN was sent over the
-	 * retran path, update the retran path and use it.
-	 */
-	if (!asoc->shutdown_last_sent_to)
+	if (!last_sent_to)
 		return asoc->peer.active_path;
 	else {
-		if (asoc->shutdown_last_sent_to == asoc->peer.retran_path)
+		if (last_sent_to == asoc->peer.retran_path)
 			sctp_assoc_update_retran_path(asoc);
 		return asoc->peer.retran_path;
 	}
-
 }
 
 /* Update the association's pmtu and frag_point by going through all the
@@ -1482,6 +1470,10 @@
 {
 	int assoc_id;
 	int error = 0;
+
+	/* If the id is already assigned, keep it. */
+	if (asoc->assoc_id)
+		return error;
 retry:
 	if (unlikely(!idr_pre_get(&sctp_assocs_id, gfp)))
 		return -ENOMEM;
diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c
index 6851ee9..61cc607 100644
--- a/net/sctp/sm_make_chunk.c
+++ b/net/sctp/sm_make_chunk.c
@@ -2864,19 +2864,19 @@
 	switch (addr_param->v4.param_hdr.type) {
 	case SCTP_PARAM_IPV6_ADDRESS:
 		if (!asoc->peer.ipv6_address)
-			return SCTP_ERROR_INV_PARAM;
+			return SCTP_ERROR_DNS_FAILED;
 		break;
 	case SCTP_PARAM_IPV4_ADDRESS:
 		if (!asoc->peer.ipv4_address)
-			return SCTP_ERROR_INV_PARAM;
+			return SCTP_ERROR_DNS_FAILED;
 		break;
 	default:
-		return SCTP_ERROR_INV_PARAM;
+		return SCTP_ERROR_DNS_FAILED;
 	}
 
 	af = sctp_get_af_specific(param_type2af(addr_param->v4.param_hdr.type));
 	if (unlikely(!af))
-		return SCTP_ERROR_INV_PARAM;
+		return SCTP_ERROR_DNS_FAILED;
 
 	af->from_addr_param(&addr, addr_param, htons(asoc->peer.port), 0);
 
@@ -2886,7 +2886,7 @@
 	 *  make sure we check for that)
 	 */
 	if (!af->is_any(&addr) && !af->addr_valid(&addr, NULL, asconf->skb))
-		return SCTP_ERROR_INV_PARAM;
+		return SCTP_ERROR_DNS_FAILED;
 
 	switch (asconf_param->param_hdr.type) {
 	case SCTP_PARAM_ADD_IP:
@@ -2954,12 +2954,12 @@
 
 		peer = sctp_assoc_lookup_paddr(asoc, &addr);
 		if (!peer)
-			return SCTP_ERROR_INV_PARAM;
+			return SCTP_ERROR_DNS_FAILED;
 
 		sctp_assoc_set_primary(asoc, peer);
 		break;
 	default:
-		return SCTP_ERROR_INV_PARAM;
+		return SCTP_ERROR_UNKNOWN_PARAM;
 		break;
 	}
 
@@ -3273,7 +3273,7 @@
 			retval = 1;
 			break;
 
-		case SCTP_ERROR_INV_PARAM:
+		case SCTP_ERROR_UNKNOWN_PARAM:
 			/* Disable sending this type of asconf parameter in
 			 * future.
 			 */
diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c
index e2020eb..86426aa 100644
--- a/net/sctp/sm_sideeffect.c
+++ b/net/sctp/sm_sideeffect.c
@@ -686,7 +686,8 @@
 {
 	struct sctp_transport *t;
 
-	t = sctp_assoc_choose_shutdown_transport(asoc);
+	t = sctp_assoc_choose_alter_transport(asoc,
+					      asoc->shutdown_last_sent_to);
 	asoc->shutdown_last_sent_to = t;
 	asoc->timeouts[SCTP_EVENT_TIMEOUT_T2_SHUTDOWN] = t->rto;
 	chunk->transport = t;
@@ -777,7 +778,7 @@
 {
 	struct sctp_transport *t;
 
-	t = asoc->peer.active_path;
+	t = sctp_assoc_choose_alter_transport(asoc, chunk->transport);
 	asoc->timeouts[SCTP_EVENT_TIMEOUT_T4_RTO] = t->rto;
 	chunk->transport = t;
 }
@@ -1379,7 +1380,8 @@
 
 		case SCTP_CMD_INIT_CHOOSE_TRANSPORT:
 			chunk = cmd->obj.ptr;
-			t = sctp_assoc_choose_init_transport(asoc);
+			t = sctp_assoc_choose_alter_transport(asoc,
+						asoc->init_last_sent_to);
 			asoc->init_last_sent_to = t;
 			chunk->transport = t;
 			t->init_sent_count++;
diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c
index 55a61aa..7288192 100644
--- a/net/sctp/sm_statefuns.c
+++ b/net/sctp/sm_statefuns.c
@@ -5432,9 +5432,13 @@
 	if (!reply)
 		goto nomem;
 
-	/* Do some failure management (Section 8.2). */
-	sctp_add_cmd_sf(commands, SCTP_CMD_STRIKE,
-			SCTP_TRANSPORT(asoc->shutdown_last_sent_to));
+	/* Do some failure management (Section 8.2).
+	 * If we remove the transport an SHUTDOWN was last sent to, don't
+	 * do failure management.
+	 */
+	if (asoc->shutdown_last_sent_to)
+		sctp_add_cmd_sf(commands, SCTP_CMD_STRIKE,
+				SCTP_TRANSPORT(asoc->shutdown_last_sent_to));
 
 	/* Set the transport for the SHUTDOWN/ACK chunk and the timeout for
 	 * the T2-shutdown timer.
@@ -5471,7 +5475,9 @@
 	 * detection on the appropriate destination address as defined in
 	 * RFC2960 [5] section 8.1 and 8.2.
 	 */
-	sctp_add_cmd_sf(commands, SCTP_CMD_STRIKE, SCTP_TRANSPORT(transport));
+	if (transport)
+		sctp_add_cmd_sf(commands, SCTP_CMD_STRIKE,
+				SCTP_TRANSPORT(transport));
 
 	/* Reconfig T4 timer and transport. */
 	sctp_add_cmd_sf(commands, SCTP_CMD_SETUP_T4, SCTP_CHUNK(chunk));
diff --git a/net/sctp/sm_statetable.c b/net/sctp/sm_statetable.c
index 5c8186d..6d9b3aa 100644
--- a/net/sctp/sm_statetable.c
+++ b/net/sctp/sm_statetable.c
@@ -698,7 +698,7 @@
 	TYPE_SCTP_FUNC(sctp_sf_do_prm_asconf), \
 	/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
 	TYPE_SCTP_FUNC(sctp_sf_error_shutdown), \
-} /* TYPE_SCTP_PRIMITIVE_REQUESTHEARTBEAT */
+} /* TYPE_SCTP_PRIMITIVE_ASCONF */
 
 /* The primary index for this table is the primitive type.
  * The secondary index for this table is the state.
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index 5fb3a8c..7c3dfd2 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -1100,6 +1100,15 @@
 		goto out_free;
 	}
 
+	/* In case the user of sctp_connectx() wants an association
+	 * id back, assign one now.
+	 */
+	if (assoc_id) {
+		err = sctp_assoc_set_id(asoc, GFP_KERNEL);
+		if (err < 0)
+			goto out_free;
+	}
+
 	err = sctp_primitive_ASSOCIATE(asoc, NULL);
 	if (err < 0) {
 		goto out_free;
@@ -1120,7 +1129,7 @@
 	timeo = sock_sndtimeo(sk, f_flags & O_NONBLOCK);
 
 	err = sctp_wait_for_connect(asoc, &timeo);
-	if (!err && assoc_id)
+	if ((err == 0 || err == -EINPROGRESS) && assoc_id)
 		*assoc_id = asoc->assoc_id;
 
 	/* Don't free association on exit. */
@@ -1264,6 +1273,34 @@
 		return assoc_id;
 }
 
+/*
+ * New (hopefully final) interface for the API.  The option buffer is used
+ * both for the returned association id and the addresses.
+ */
+SCTP_STATIC int sctp_getsockopt_connectx3(struct sock* sk, int len,
+					char __user *optval,
+					int __user *optlen)
+{
+	sctp_assoc_t assoc_id = 0;
+	int err = 0;
+
+	if (len < sizeof(assoc_id))
+		return -EINVAL;
+
+	err = __sctp_setsockopt_connectx(sk,
+			(struct sockaddr __user *)(optval + sizeof(assoc_id)),
+			len - sizeof(assoc_id), &assoc_id);
+
+	if (err == 0 || err == -EINPROGRESS) {
+		if (copy_to_user(optval, &assoc_id, sizeof(assoc_id)))
+			return -EFAULT;
+		if (put_user(sizeof(assoc_id), optlen))
+			return -EFAULT;
+	}
+
+	return err;
+}
+
 /* API 3.1.4 close() - UDP Style Syntax
  * Applications use close() to perform graceful shutdown (as described in
  * Section 10.1 of [SCTP]) on ALL the associations currently represented
@@ -5578,6 +5615,9 @@
 		retval = sctp_getsockopt_local_addrs(sk, len, optval,
 						     optlen);
 		break;
+	case SCTP_SOCKOPT_CONNECTX3:
+		retval = sctp_getsockopt_connectx3(sk, len, optval, optlen);
+		break;
 	case SCTP_DEFAULT_SEND_PARAM:
 		retval = sctp_getsockopt_default_send_param(sk, len,
 							    optval, optlen);
diff --git a/net/sctp/sysctl.c b/net/sctp/sysctl.c
index f58e994..63eabbc 100644
--- a/net/sctp/sysctl.c
+++ b/net/sctp/sysctl.c
@@ -49,8 +49,8 @@
 static int one = 1;
 static int timer_max = 86400000; /* ms in one day */
 static int int_max = INT_MAX;
-static long sack_timer_min = 1;
-static long sack_timer_max = 500;
+static int sack_timer_min = 1;
+static int sack_timer_max = 500;
 
 extern int sysctl_sctp_mem[3];
 extern int sysctl_sctp_rmem[3];
@@ -223,7 +223,7 @@
 		.ctl_name	= NET_SCTP_SACK_TIMEOUT,
 		.procname	= "sack_timeout",
 		.data		= &sctp_sack_timeout,
-		.maxlen		= sizeof(long),
+		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
 		.strategy	= sysctl_intvec,