Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8a07eb0a authored by Michio Honda's avatar Michio Honda Committed by David S. Miller
Browse files

sctp: Add ASCONF operation on the single-homed host



In this case, the SCTP association transmits an ASCONF packet
including addition of the new IP address and deletion of the old
address.  This patch implements this functionality.
In this case, the ASCONF chunk is added to the beginning of the
queue, because the other chunks cannot be transmitted in this state.

Signed-off-by: default avatarMichio Honda <micchie@sfc.wide.ad.jp>
Signed-off-by: default avatarYOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Acked-by: default avatarWei Yongjun <yjwei@cn.fujitsu.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 7dc04d71
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -1913,6 +1913,8 @@ struct sctp_association {
	 * after reaching 4294967295.
	 */
	__u32 addip_serial;
	union sctp_addr *asconf_addr_del_pending;
	int src_out_of_asoc_ok;

	/* SCTP AUTH: list of the endpoint shared keys.  These
	 * keys are provided out of band by the user applicaton
+6 −0
Original line number Diff line number Diff line
@@ -280,6 +280,8 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a
	asoc->peer.asconf_capable = 0;
	if (sctp_addip_noauth)
		asoc->peer.asconf_capable = 1;
	asoc->asconf_addr_del_pending = NULL;
	asoc->src_out_of_asoc_ok = 0;

	/* Create an input queue.  */
	sctp_inq_init(&asoc->base.inqueue);
@@ -446,6 +448,10 @@ void sctp_association_free(struct sctp_association *asoc)

	sctp_asconf_queue_teardown(asoc);

	/* Free pending address space being deleted */
	if (asoc->asconf_addr_del_pending != NULL)
		kfree(asoc->asconf_addr_del_pending);

	/* AUTH - Free the endpoint shared keys */
	sctp_auth_destroy_keys(&asoc->endpoint_shared_keys);

+13 −0
Original line number Diff line number Diff line
@@ -754,6 +754,16 @@ static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
	 */

	list_for_each_entry_safe(chunk, tmp, &q->control_chunk_list, list) {
		/* RFC 5061, 5.3
		 * F1) This means that until such time as the ASCONF
		 * containing the add is acknowledged, the sender MUST
		 * NOT use the new IP address as a source for ANY SCTP
		 * packet except on carrying an ASCONF Chunk.
		 */
		if (asoc->src_out_of_asoc_ok &&
		    chunk->chunk_hdr->type != SCTP_CID_ASCONF)
			continue;

		list_del_init(&chunk->list);

		/* Pick the right transport to use. */
@@ -881,6 +891,9 @@ static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
		}
	}

	if (q->asoc->src_out_of_asoc_ok)
		goto sctp_flush_out;

	/* Is it OK to send data chunks?  */
	switch (asoc->state) {
	case SCTP_STATE_COOKIE_ECHOED:
+3 −1
Original line number Diff line number Diff line
@@ -503,7 +503,9 @@ static void sctp_v4_get_dst(struct sctp_transport *t, union sctp_addr *saddr,
		sctp_v4_dst_saddr(&dst_saddr, fl4, htons(bp->port));
		rcu_read_lock();
		list_for_each_entry_rcu(laddr, &bp->address_list, list) {
			if (!laddr->valid || (laddr->state != SCTP_ADDR_SRC))
			if (!laddr->valid || (laddr->state == SCTP_ADDR_DEL) ||
			    (laddr->state != SCTP_ADDR_SRC &&
			    !asoc->src_out_of_asoc_ok))
				continue;
			if (sctp_v4_cmp_addr(&dst_saddr, &laddr->a))
				goto out_unlock;
+27 −0
Original line number Diff line number Diff line
@@ -2768,6 +2768,7 @@ struct sctp_chunk *sctp_make_asconf_update_ip(struct sctp_association *asoc,
	int			addr_param_len = 0;
	int 			totallen = 0;
	int 			i;
	int			del_pickup = 0;

	/* Get total length of all the address parameters. */
	addr_buf = addrs;
@@ -2780,6 +2781,13 @@ struct sctp_chunk *sctp_make_asconf_update_ip(struct sctp_association *asoc,
		totallen += addr_param_len;

		addr_buf += af->sockaddr_len;
		if (asoc->asconf_addr_del_pending && !del_pickup) {
			/* reuse the parameter length from the same scope one */
			totallen += paramlen;
			totallen += addr_param_len;
			del_pickup = 1;
			SCTP_DEBUG_PRINTK("mkasconf_update_ip: picked same-scope del_pending addr, totallen for all addresses is %d\n", totallen);
		}
	}

	/* Create an asconf chunk with the required length. */
@@ -2802,6 +2810,17 @@ struct sctp_chunk *sctp_make_asconf_update_ip(struct sctp_association *asoc,

		addr_buf += af->sockaddr_len;
	}
	if (flags == SCTP_PARAM_ADD_IP && del_pickup) {
		addr = asoc->asconf_addr_del_pending;
		af = sctp_get_af_specific(addr->v4.sin_family);
		addr_param_len = af->to_addr_param(addr, &addr_param);
		param.param_hdr.type = SCTP_PARAM_DEL_IP;
		param.param_hdr.length = htons(paramlen + addr_param_len);
		param.crr_id = i;

		sctp_addto_chunk(retval, paramlen, &param);
		sctp_addto_chunk(retval, addr_param_len, &addr_param);
	}
	return retval;
}

@@ -3224,6 +3243,11 @@ static void sctp_asconf_param_success(struct sctp_association *asoc,
	case SCTP_PARAM_DEL_IP:
		local_bh_disable();
		sctp_del_bind_addr(bp, &addr);
		if (asoc->asconf_addr_del_pending != NULL &&
		    sctp_cmp_addr_exact(asoc->asconf_addr_del_pending, &addr)) {
			kfree(asoc->asconf_addr_del_pending);
			asoc->asconf_addr_del_pending = NULL;
		}
		local_bh_enable();
		list_for_each_entry(transport, &asoc->peer.transport_addr_list,
				transports) {
@@ -3381,6 +3405,9 @@ int sctp_process_asconf_ack(struct sctp_association *asoc,
		asconf_len -= length;
	}

	if (no_err && asoc->src_out_of_asoc_ok)
		asoc->src_out_of_asoc_ok = 0;

	/* Free the cached last sent asconf chunk. */
	list_del_init(&asconf->transmitted_list);
	sctp_chunk_free(asconf);
Loading