sctp_disposition_t sctp_sf_do_5_1C_ack(const struct sctp_endpoint *ep,
				       const struct sctp_association *asoc,
				       const sctp_subtype_t type,
				       void *arg,
				       sctp_cmd_seq_t *commands)
{
	struct sctp_chunk *chunk = arg;
	sctp_init_chunk_t *initchunk;
	__u32 init_tag;
	struct sctp_chunk *err_chunk;
	struct sctp_packet *packet;
	sctp_disposition_t ret;

	if (!sctp_vtag_verify(chunk, asoc))
		return sctp_sf_pdiscard(ep, asoc, type, arg, commands);

	/* Make sure that the INIT-ACK chunk has a valid length */
	if (!sctp_chunk_length_valid(chunk, sizeof(sctp_initack_chunk_t)))
		return sctp_sf_violation_chunklen(ep, asoc, type, arg,
						  commands);
	/* 6.10 Bundling
	 * An endpoint MUST NOT bundle INIT, INIT ACK or
	 * SHUTDOWN COMPLETE with any other chunks.
	 */
	if (!chunk->singleton)
		return SCTP_DISPOSITION_VIOLATION;

	/* Grab the INIT header.  */
	chunk->subh.init_hdr = (sctp_inithdr_t *) chunk->skb->data;

	init_tag = ntohl(chunk->subh.init_hdr->init_tag);

	/* Verification Tag: 3.3.3
	 *   If the value of the Initiate Tag in a received INIT ACK
	 *   chunk is found to be 0, the receiver MUST treat it as an
	 *   error and close the association by transmitting an ABORT.
	 */
	if (!init_tag) {
		struct sctp_chunk *reply = sctp_make_abort(asoc, chunk, 0);
		if (!reply)
			goto nomem;

		sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(reply));
		sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
				SCTP_STATE(SCTP_STATE_CLOSED));
		SCTP_INC_STATS(SCTP_MIB_ABORTEDS);
		sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, SCTP_NULL());
		return SCTP_DISPOSITION_DELETE_TCB;
	}

	/* Verify the INIT chunk before processing it. */
	err_chunk = NULL;
	if (!sctp_verify_init(asoc, chunk->chunk_hdr->type,
			      (sctp_init_chunk_t *)chunk->chunk_hdr, chunk,
			      &err_chunk)) {

		SCTP_INC_STATS(SCTP_MIB_ABORTEDS);

		/* This chunk contains fatal error. It is to be discarded.
		 * Send an ABORT, with causes if there is any.
		 */
		if (err_chunk) {
			packet = sctp_abort_pkt_new(ep, asoc, arg,
					(__u8 *)(err_chunk->chunk_hdr) +
					sizeof(sctp_chunkhdr_t),
					ntohs(err_chunk->chunk_hdr->length) -
					sizeof(sctp_chunkhdr_t));

			sctp_chunk_free(err_chunk);

			if (packet) {
				sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT,
						SCTP_PACKET(packet));
				SCTP_INC_STATS(SCTP_MIB_OUTCTRLCHUNKS);
				sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
						SCTP_STATE(SCTP_STATE_CLOSED));
				sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB,
						SCTP_NULL());
				return SCTP_DISPOSITION_CONSUME;
			} else {
				sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
						SCTP_STATE(SCTP_STATE_CLOSED));
				sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB,
						SCTP_NULL());
				return SCTP_DISPOSITION_NOMEM;
			}
		} else {
			ret = sctp_sf_tabort_8_4_8(ep, asoc, type, arg,
						   commands);
			sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
					SCTP_STATE(SCTP_STATE_CLOSED));
			sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB,
					SCTP_NULL());
			return ret;
		}
	}

	/* Tag the variable length parameters.  Note that we never
	 * convert the parameters in an INIT chunk.
	 */
	chunk->param_hdr.v = skb_pull(chunk->skb, sizeof(sctp_inithdr_t));

	initchunk = (sctp_init_chunk_t *) chunk->chunk_hdr;

	sctp_add_cmd_sf(commands, SCTP_CMD_PEER_INIT,
			SCTP_PEER_INIT(initchunk));

	/* Reset init error count upon receipt of INIT-ACK.  */
	sctp_add_cmd_sf(commands, SCTP_CMD_INIT_COUNTER_RESET, SCTP_NULL());

	/* 5.1 C) "A" shall stop the T1-init timer and leave
	 * COOKIE-WAIT state.  "A" shall then ... start the T1-cookie
	 * timer, and enter the COOKIE-ECHOED state.
	 */
	sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP,
			SCTP_TO(SCTP_EVENT_TIMEOUT_T1_INIT));
	sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_START,
			SCTP_TO(SCTP_EVENT_TIMEOUT_T1_COOKIE));
	sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
			SCTP_STATE(SCTP_STATE_COOKIE_ECHOED));

	/* 5.1 C) "A" shall then send the State Cookie received in the
	 * INIT ACK chunk in a COOKIE ECHO chunk, ...
	 */
	/* If there is any errors to report, send the ERROR chunk generated
	 * for unknown parameters as well.
	 */
	sctp_add_cmd_sf(commands, SCTP_CMD_GEN_COOKIE_ECHO,
			SCTP_CHUNK(err_chunk));

	return SCTP_DISPOSITION_CONSUME;

nomem:
	return SCTP_DISPOSITION_NOMEM;
}
sctp_disposition_t sctp_sf_do_5_1C_ack(const struct sctp_endpoint *ep,
				       const struct sctp_association *asoc,
				       const sctp_subtype_t type,
				       void *arg,
				       sctp_cmd_seq_t *commands)
{
	struct sctp_chunk *chunk = arg;
	sctp_init_chunk_t *initchunk;
	__u32 init_tag;
	struct sctp_chunk *err_chunk;
	struct sctp_packet *packet;
	sctp_disposition_t ret;

	if (!sctp_vtag_verify(chunk, asoc))
		return sctp_sf_pdiscard(ep, asoc, type, arg, commands);

	 
	if (!sctp_chunk_length_valid(chunk, sizeof(sctp_initack_chunk_t)))
		return sctp_sf_violation_chunklen(ep, asoc, type, arg,
						  commands);
	 
	if (!chunk->singleton)
		return SCTP_DISPOSITION_VIOLATION;

	 
	chunk->subh.init_hdr = (sctp_inithdr_t *) chunk->skb->data;

	init_tag = ntohl(chunk->subh.init_hdr->init_tag);

	 
	if (!init_tag) {
		struct sctp_chunk *reply = sctp_make_abort(asoc, chunk, 0);
		if (!reply)
			goto nomem;

		sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(reply));
		sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
				SCTP_STATE(SCTP_STATE_CLOSED));
		SCTP_INC_STATS(SCTP_MIB_ABORTEDS);
		sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, SCTP_NULL());
		return SCTP_DISPOSITION_DELETE_TCB;
	}

	 
	err_chunk = NULL;
	if (!sctp_verify_init(asoc, chunk->chunk_hdr->type,
			      (sctp_init_chunk_t *)chunk->chunk_hdr, chunk,
			      &err_chunk)) {

		SCTP_INC_STATS(SCTP_MIB_ABORTEDS);

		 
		if (err_chunk) {
			packet = sctp_abort_pkt_new(ep, asoc, arg,
					(__u8 *)(err_chunk->chunk_hdr) +
					sizeof(sctp_chunkhdr_t),
					ntohs(err_chunk->chunk_hdr->length) -
					sizeof(sctp_chunkhdr_t));

			sctp_chunk_free(err_chunk);

			if (packet) {
				sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT,
						SCTP_PACKET(packet));
				SCTP_INC_STATS(SCTP_MIB_OUTCTRLCHUNKS);
				sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
						SCTP_STATE(SCTP_STATE_CLOSED));
				sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB,
						SCTP_NULL());
				return SCTP_DISPOSITION_CONSUME;
			} else {
				sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
						SCTP_STATE(SCTP_STATE_CLOSED));
				sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB,
						SCTP_NULL());
				return SCTP_DISPOSITION_NOMEM;
			}
		} else {
			ret = sctp_sf_tabort_8_4_8(ep, asoc, type, arg,
						   commands);
			sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
					SCTP_STATE(SCTP_STATE_CLOSED));
			sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB,
					SCTP_NULL());
			return ret;
		}
	}

	 
	chunk->param_hdr.v = skb_pull(chunk->skb, sizeof(sctp_inithdr_t));

	initchunk = (sctp_init_chunk_t *) chunk->chunk_hdr;

	sctp_add_cmd_sf(commands, SCTP_CMD_PEER_INIT,
			SCTP_PEER_INIT(initchunk));

	 
	sctp_add_cmd_sf(commands, SCTP_CMD_INIT_COUNTER_RESET, SCTP_NULL());

	 
	sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP,
			SCTP_TO(SCTP_EVENT_TIMEOUT_T1_INIT));
	sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_START,
			SCTP_TO(SCTP_EVENT_TIMEOUT_T1_COOKIE));
	sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
			SCTP_STATE(SCTP_STATE_COOKIE_ECHOED));

	 
	 
	sctp_add_cmd_sf(commands, SCTP_CMD_GEN_COOKIE_ECHO,
			SCTP_CHUNK(err_chunk));

	return SCTP_DISPOSITION_CONSUME;

nomem:
	return SCTP_DISPOSITION_NOMEM;
}