Skip to content
Snippets Groups Projects
sctp_chunk_to_string.c 52.8 KiB
Newer Older
/*
 * Copyright 2015 Michael Tuexen
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA.
 */
/*
 * Author: tuexen@fh-muenster.de (Michael Tuexen)
 *
 * Implementation for generating human-readable representations of SCTP chunks.
 */

#include "sctp_chunk_to_string.h"
#include "sctp_iterator.h"

static int sctp_parameter_to_string(FILE *, struct sctp_parameter *, char **);

static int sctp_heartbeat_information_parameter_to_string(
	FILE *s,
	struct sctp_heartbeat_information_parameter *parameter,
	char **error)
{
	u16 length;

	length = ntohs(parameter->length);
	if (length < sizeof(struct sctp_heartbeat_information_parameter)) {
		asprintf(error, "HEARTBEAT_INFORMATION parameter illegal (length=%u)",
			 length);
		return STATUS_ERR;
	}
	fprintf(s, "HEARTBEAT_INFORMATION[len=%u, val=...]", length);
	return STATUS_OK;
}

static int sctp_ipv4_address_parameter_to_string(
	FILE *s,
	struct sctp_ipv4_address_parameter *parameter,
	char **error)
{
	u16 length;
	char buffer[INET_ADDRSTRLEN];

	length = ntohs(parameter->length);
	if (length != sizeof(struct sctp_ipv4_address_parameter)) {
		asprintf(error, "IPV4_ADDRESS parameter illegal (length=%u)",
			 length);
		return STATUS_ERR;
	}
	inet_ntop(AF_INET, &parameter->addr, buffer, INET_ADDRSTRLEN);
	fprintf(s, "IPV4_ADDRESS[addr=%s]", buffer);
	return STATUS_OK;
}

static int sctp_ipv6_address_parameter_to_string(
	FILE *s,
	struct sctp_ipv6_address_parameter *parameter,
	char **error)
{
	u16 length;
	char buffer[INET6_ADDRSTRLEN];

	length = ntohs(parameter->length);
	if (length != sizeof(struct sctp_ipv6_address_parameter)) {
		asprintf(error, "IPV6_ADDRESS parameter illegal (length=%u)",
			 length);
		return STATUS_ERR;
	}
	inet_ntop(AF_INET6, &parameter->addr, buffer, INET6_ADDRSTRLEN);
	fprintf(s, "IPV6_ADDRESS[addr=%s]", buffer);
	return STATUS_OK;
}

static int sctp_state_cookie_parameter_to_string(
	FILE *s,
	struct sctp_state_cookie_parameter *parameter,
	char **error)
{

	length = ntohs(parameter->length);
	if (length < sizeof(struct sctp_state_cookie_parameter)) {
		asprintf(error, "STATE_COOKIE parameter illegal (length=%u)",
			 length);
		return STATUS_ERR;
	}
	fprintf(s, "STATE_COOKIE[len=%d, val=...]", length);
	return STATUS_OK;
}

static int sctp_unrecognized_parameter_parameter_to_string(
	FILE *s,
	struct sctp_unrecognized_parameter_parameter *parameter,
	char **error)
{
	u16 length;
	int result = STATUS_OK;

	length = ntohs(parameter->length);
	if (length < (sizeof(struct sctp_unrecognized_parameter_parameter) +
		      sizeof(struct sctp_parameter))) {
		asprintf(error,
			 "UNRECOGNIZED_PARAMETER parameter illegal (length=%u)",
			 length);
		return STATUS_ERR;
	}
	fputs("UNRECOGNIZED_PARAMETER[params=[", s);
	result = sctp_parameter_to_string(s,
		(struct sctp_parameter *)parameter->value, error);
	fputs("]]", s);
	return result;
}

static int sctp_cookie_preservative_parameter_to_string(
	FILE *s,
	struct sctp_cookie_preservative_parameter *parameter,
	char **error)
{
	u16 length;

	length = ntohs(parameter->length);
	if (length != sizeof(struct sctp_cookie_preservative_parameter)) {
		asprintf(error,
			 "COOKIE_PRESERVATIVE parameter illegal (length=%u)",
			 length);
		return STATUS_ERR;
	}
	fputs("COOKIE_PRESERVATIVE[incr=", s);
	fprintf(s, "%u", ntohl(parameter->increment));
	fputc(']', s);
	return STATUS_OK;
}

static int sctp_hostname_parameter_to_string(
	FILE *s,
	struct sctp_hostname_address_parameter *parameter,
	char **error)
{
	u16 length;

	length = ntohs(parameter->length);
	if (length < sizeof(struct sctp_hostname_address_parameter)) {
		asprintf(error, "HOSTNAME_ADDRESS parameter illegal (length=%u)",
	fprintf(s, "HOSTNAME_ADDRESS[addr=\"%.*s\"]",
		(int)(length - sizeof(struct sctp_hostname_address_parameter)),
		(char *)parameter->hostname);
	return STATUS_OK;
}

static int sctp_supported_address_types_parameter_to_string(
	FILE *s,
	struct sctp_supported_address_types_parameter *parameter,
	char **error)
{
	u16 i, length, nr_address_types;

	length = ntohs(parameter->length);
	if ((length < sizeof(struct sctp_supported_address_types_parameter)) ||
	    ((length & 0x0001) != 0)) {
		asprintf(error,
			 "SUPPORTED_ADDRESS_TYPES parameter illegal (length=%u)",
			 length);
		return STATUS_ERR;
	}
	nr_address_types =
		(length - sizeof(struct sctp_supported_address_types_parameter))
		/ sizeof(u16);
	fputs("SUPPORTED_ADDRESS_TYPES[types=[", s);
	for (i = 0; i < nr_address_types; i++) {
		if (i > 0)
			fputs(", ", s);
		switch (ntohs(parameter->address_type[i])) {
		case SCTP_IPV4_ADDRESS_PARAMETER_TYPE:
			fputs("IPv4", s);
			break;
		case SCTP_IPV6_ADDRESS_PARAMETER_TYPE:
			fputs("IPv6", s);
			break;
		case SCTP_HOSTNAME_ADDRESS_PARAMETER_TYPE:
			fputs("HOSTNAME", s);
			break;
		default:
			fprintf(s, "0x%04x", ntohs(parameter->address_type[i]));
			break;
		}
	}
	fputs("]]", s);
static int sctp_outgoing_ssn_reset_request_parameter_to_string(
	FILE *s,
	struct sctp_outgoing_ssn_reset_request_parameter *parameter,
	char **error)
{
	u16 length;
	u32 reqsn;
	u32 respsn;
	u32 last_tsn;
	int len;

	length = ntohs(parameter->length);
	if (length < sizeof(struct sctp_outgoing_ssn_reset_request_parameter)) {
		fputs("invalid OUTGOING_SSN_RESET_REQUEST parameter", s);
		asprintf(error, "OUTGOING_SSN_RESET_REQUEST parameter illegal (length=%u)",
			 length);
		return STATUS_ERR;
	}
	reqsn = ntohl(parameter->reqsn);
	respsn = ntohl(parameter->respsn);
	last_tsn = ntohl(parameter->last_tsn);
	fputs("OUTGOING_SSN_RESET[", s);
	fprintf(s, "len=%hu, ", length);
	fprintf(s, "req_sn=%u, ", reqsn);
	fprintf(s, "resp_sn=%u, ", respsn);
	fprintf(s, "last_tsn=%u, ", last_tsn);
	fputs("sids=[", s);
	for(len = 0; len < ((length-16)/sizeof(u16)); len++) {
		u16 sid;
Hoelscher's avatar
Hoelscher committed
		sid = ntohs(parameter->sids[len]);
		if (len > 0)
			fprintf(s, ", ");
Hoelscher's avatar
Hoelscher committed
		fprintf(s, "%hu", sid);
	}
	fputs("]", s);
	return STATUS_OK;
}

static int sctp_incoming_ssn_reset_request_parameter_to_string(
	FILE *s,
	struct sctp_incoming_ssn_reset_request_parameter *parameter,
	char **error)
{
	u16 length;
	u32 reqsn;
	int len;

	length = ntohs(parameter->length);
	if (length < sizeof(struct sctp_incoming_ssn_reset_request_parameter)) {
		fputs("invalid INCOMING_SSN_RESET_REQUEST parameter", s);
		asprintf(error, "INCOMING_SSN_RESET_REQUEST parameter illegal (length=%u)",
			 length);
		return STATUS_ERR;
	}
	reqsn = ntohl(parameter->reqsn);
	fputs("INCOMING_SSN_RESET[", s);
	fprintf(s, "len=%hu, ", length);
	fprintf(s, "req_sn=%u, ", reqsn);
	fputs("sids=[", s);
	for(len = 0; len < ((length-8)/sizeof(u16)); len++) {
		u16 sid;
		sid = ntohs(parameter->sids[len]);
		if (len > 0)
			fprintf(s, ", ");
Hoelscher's avatar
Hoelscher committed
		fprintf(s, "%hu", sid);
static int sctp_ssn_tsn_reset_request_parameter_to_string(
	FILE *s,
	struct sctp_ssn_tsn_reset_request_parameter *parameter,
	char **error)
{
	u16 length;
	u32 reqsn;

	length = ntohs(parameter->length);
	if (length != sizeof(struct sctp_ssn_tsn_reset_request_parameter)) {
		fputs("invalid SSN_TSN_RESET_REQUEST parameter", s);
		asprintf(error, "SSN_TSN_RESET_REQUEST parameter illegal (length=%u)",
			 length);
		return STATUS_ERR;
	}
	reqsn = ntohl(parameter->reqsn);

	fputs("SSN_TSN_RESET[", s);
	fprintf(s, "len=%hu, ", length);
	fprintf(s, "req_sn=%u", reqsn);
	fputs("]", s);
	return STATUS_OK;
}

static int sctp_reconfig_response_parameter_to_string(
	FILE *s,
	struct sctp_reconfig_response_parameter *parameter,
	char **error)
{
	u16 length;
	u32 respsn;
	u32 sender_next_tsn;
	u32 receiver_next_tsn;

	length = ntohs(parameter->length);
	// filter correct length
	if ((length != sizeof(struct sctp_reconfig_response_parameter)) &&
	    (length != sizeof(struct sctp_reconfig_response_parameter) - 8)) {
		fputs("invalid RECONFIG_RESPONSE parameter", s);
		asprintf(error, "RECONFIG_RESPONSE parameter illegal (length=%u)",
		         length);
		return STATUS_ERR;
	}
	respsn = ntohl(parameter->respsn);
	fputs("RECONFIG_RESPONSE[", s);
	fprintf(s, "len=%hu, ", length);
	fprintf(s, "resp_sn=%u, ", respsn);
	fprintf(s, "result=%u", result);
	if (length == sizeof(struct sctp_reconfig_response_parameter)){
		sender_next_tsn = ntohl(parameter->sender_next_tsn);
		receiver_next_tsn = ntohl(parameter->receiver_next_tsn);
		fprintf(s, ", sender_next_tsn=%u, ", sender_next_tsn);
		fprintf(s, "receiver_next_tsn=%u", receiver_next_tsn);
	}
	fputs("]", s);
	return STATUS_OK;
}

static int sctp_add_outgoing_streams_request_parameter_to_string(
	FILE *s,
	struct sctp_add_outgoing_streams_request_parameter *parameter,
	char **error)
{
	u16 length;
	u32 reqsn;
	u16 number_of_new_streams;

	length = ntohs(parameter->length);
	if (length != sizeof(struct sctp_add_outgoing_streams_request_parameter)) {
		fputs("invalid ADD_OUTGOING_STREAMS_REQUEST parameter", s);
		asprintf(error, "ADD_OUTGOING_STREAMS_REQUEST parameter illegal (length=%u)",
			 length);
		return STATUS_ERR;
	}
	reqsn = ntohl(parameter->reqsn);
	number_of_new_streams = ntohs(parameter->number_of_new_streams);
	reserved = ntohs(parameter->reserved);

	fputs("ADD_OUTGOING_STREAMS[", s);
	fprintf(s, "len=%hu, ", length);
	fprintf(s, "req_sn=%u, ", reqsn);
	fprintf(s, "number_of_new_streams=%hu, ", number_of_new_streams);
	fprintf(s, "reserved=%hu", reserved);
	fputs("]", s);
	return STATUS_OK;
}

static int sctp_add_incoming_streams_request_parameter_to_string(
	FILE *s,
	struct sctp_add_incoming_streams_request_parameter *parameter,
	char **error)
{
	u16 length;
	u32 reqsn;
	u16 number_of_new_streams;
	if (length != sizeof(struct sctp_add_incoming_streams_request_parameter)) {
		fputs("invalid ADD_INCOMING_STREAMS_REQUEST parameter", s);
		asprintf(error, "ADD_INCOMING_STREAMS_REQUEST parameter illegal (length=%u)",
			 length);
		return STATUS_ERR;
	}
	reqsn = ntohl(parameter->reqsn);
	number_of_new_streams = ntohs(parameter->number_of_new_streams);
	reserved = ntohs(parameter->reserved);

	fputs("ADD_INCOMING_STREAMS[", s);
	fprintf(s, "len=%hu, ", length);
	fprintf(s, "req_sn=%u, ", reqsn);
	fprintf(s, "number_of_new_streams=%hu, ", number_of_new_streams);
	fprintf(s, "reserved=%hu", reserved);
static int sctp_ecn_capable_parameter_to_string(
	FILE *s,
	struct sctp_ecn_capable_parameter *parameter,
	char **error)
{
	u16 length;

	length = ntohs(parameter->length);
	if (length != sizeof(struct sctp_ecn_capable_parameter)) {
		asprintf(error, "ECN_CAPABLE parameter illegal (length=%u)",
			 length);
		return STATUS_ERR;
	}
	fputs("ECN_CAPABLE[]", s);
	return STATUS_OK;
}

static int sctp_supported_extensions_parameter_to_string(
	FILE *s,
	struct sctp_supported_extensions_parameter *parameter,
	char **error)
{
	u16 length, nr_chunk_types, i;

	length = ntohs(parameter->length);
	if (length < sizeof(struct sctp_supported_extensions_parameter)) {
		asprintf(error,
			 "SUPPORTED_EXTENSIONS parameter illegal (length=%u)",
			 length);
		return STATUS_ERR;
	}
	nr_chunk_types = length - sizeof(struct sctp_supported_extensions_parameter);
	fputs("SUPPORTED_EXTENSIONS[types=[", s);
	for (i = 0; i < nr_chunk_types; i++) {
		if (i > 0)
			fputs(", ", s);
		switch (parameter->chunk_type[i]) {
		case SCTP_DATA_CHUNK_TYPE:
			fputs("DATA", s);
			break;
		case SCTP_INIT_CHUNK_TYPE:
			fputs("INIT", s);
			break;
		case SCTP_INIT_ACK_CHUNK_TYPE:
			fputs("INIT_ACK", s);
			break;
		case SCTP_SACK_CHUNK_TYPE:
			fputs("SACK", s);
			break;
		case SCTP_HEARTBEAT_CHUNK_TYPE:
			fputs("HEARTBEAT", s);
			break;
		case SCTP_HEARTBEAT_ACK_CHUNK_TYPE:
			fputs("HEARTBEAT_ACK", s);
			break;
		case SCTP_ABORT_CHUNK_TYPE:
			fputs("ABORT", s);
			break;
		case SCTP_SHUTDOWN_CHUNK_TYPE:
			fputs("SHUTDOWN", s);
			break;
		case SCTP_SHUTDOWN_ACK_CHUNK_TYPE:
			fputs("SHUTDOWN_ACK", s);
			break;
		case SCTP_ERROR_CHUNK_TYPE:
			fputs("ERROR", s);
			break;
		case SCTP_COOKIE_ECHO_CHUNK_TYPE:
			fputs("COOKIE_ECHO", s);
			break;
		case SCTP_COOKIE_ACK_CHUNK_TYPE:
			fputs("COOKIE_ACK", s);
			break;
		case SCTP_ECNE_CHUNK_TYPE:
			fputs("ECNE", s);
			break;
		case SCTP_CWR_CHUNK_TYPE:
			fputs("CWR", s);
			break;
		case SCTP_SHUTDOWN_COMPLETE_CHUNK_TYPE:
			fputs("SHUTDOWN_COMPLETE", s);
			break;
		case SCTP_NR_SACK_CHUNK_TYPE:
			fputs("NR_SACK", s);
			break;
		case SCTP_I_DATA_CHUNK_TYPE:
			fputs("I_DATA", s);
			break;
		case SCTP_RECONFIG_CHUNK_TYPE:
			fputs("RECONFIG", s);
			break;
		case SCTP_PAD_CHUNK_TYPE:
			fputs("PAD", s);
			break;
		case SCTP_FORWARD_TSN_CHUNK_TYPE:
			fputs("FORWARD_TSN", s);
			break;
		case SCTP_I_FORWARD_TSN_CHUNK_TYPE:
			fputs("I_FORWARD_TSN", s);
			break;
		default:
			fprintf(s, "0x%02x", parameter->chunk_type[i]);
			break;
		}
	}
	fputs("]]", s);
	return STATUS_OK;
}

static int sctp_pad_parameter_to_string(
	FILE *s,
	struct sctp_pad_parameter *parameter,
	char **error)
{
	u16 length;

	length = ntohs(parameter->length);
	fputs("PAD[", s);
	fprintf(s, "len=%u, ", length);
	fputs("val=...]", s);
	return STATUS_OK;
}

static int sctp_forward_tsn_supported_parameter_to_string(
	FILE *s,
	struct sctp_forward_tsn_supported_parameter *parameter,
	char **error)
{
	u16 length;

	length = ntohs(parameter->length);
	if (length != sizeof(struct sctp_forward_tsn_supported_parameter)) {
		asprintf(error, "FORWARD_TSN_SUPPORTED parameter illegal (length=%u)",
			 length);
		return STATUS_ERR;
	}
	fputs("FORWARD_TSN_SUPPORTED[]", s);
	return STATUS_OK;
}

static int sctp_unknown_parameter_to_string(
	FILE *s,
	struct sctp_parameter *parameter,
	char **error)
{
	u16 i, length;

	length = ntohs(parameter->length);
	if (length < sizeof(struct sctp_parameter)) {
		asprintf(error, "PARAMETER too short (type=0x%04x, length=%u)",
			 ntohs(parameter->type), length);
		return STATUS_ERR;
	}
	fputs("PARAMETER[", s);
	fprintf(s, "type=0x%04x, ", ntohs(parameter->type));
	fputs("value=[", s);
	for (i = 0; i < length - sizeof(struct sctp_parameter); i++) {
		fprintf(s, "%s0x%02x",
			   i > 0 ? ", " : "",
			   parameter->value[i]);
	}
	fputs("]]", s);
	return STATUS_OK;
}

static int sctp_adaptation_indication_parameter_to_string(
	FILE *s,
	struct sctp_adaptation_indication_parameter *parameter,
	char **error)
{
	u16 length;
hoelscher's avatar
hoelscher committed

	length = ntohs(parameter->length);
hoelscher's avatar
hoelscher committed
	if (length < sizeof(struct sctp_adaptation_indication_parameter)) {
		asprintf(error, "PARAMETER too short (type=0x%04x, length=%u)",
			 ntohs(parameter->type), length);
		return STATUS_ERR;
	}
	fputs("ADAPTATION_INDICATION[", s);
	fprintf(s, "type=0x%04x, ", ntohs(parameter->type));
	fprintf(s, "len=%hu, ", ntohs(parameter->length));
hoelscher's avatar
hoelscher committed
	fprintf(s, "val=%u", ntohl(parameter->adaptation_code_point));
static int sctp_parameter_to_string(FILE *s,
				    struct sctp_parameter *parameter,
				    char **error)
{
	int result;
hoelscher's avatar
hoelscher committed

	switch (ntohs(parameter->type)) {
	case SCTP_HEARTBEAT_INFORMATION_PARAMETER_TYPE:
		result = sctp_heartbeat_information_parameter_to_string(s,
			(struct sctp_heartbeat_information_parameter *)parameter, error);
		break;
	case SCTP_IPV4_ADDRESS_PARAMETER_TYPE:
		result = sctp_ipv4_address_parameter_to_string(s,
			(struct sctp_ipv4_address_parameter *)parameter, error);
		break;
	case SCTP_IPV6_ADDRESS_PARAMETER_TYPE:
		result = sctp_ipv6_address_parameter_to_string(s,
			(struct sctp_ipv6_address_parameter *)parameter, error);
		break;
	case SCTP_STATE_COOKIE_PARAMETER_TYPE:
		result = sctp_state_cookie_parameter_to_string(s,
			(struct sctp_state_cookie_parameter *)parameter, error);
		break;
	case SCTP_UNRECOGNIZED_PARAMETER_PARAMETER_TYPE:
		result = sctp_unrecognized_parameter_parameter_to_string(s,
			(struct sctp_unrecognized_parameter_parameter *)parameter,
			error);
		break;
	case SCTP_COOKIE_PRESERVATIVE_PARAMETER_TYPE:
		result = sctp_cookie_preservative_parameter_to_string(s,
			(struct sctp_cookie_preservative_parameter *)parameter,
			error);
		break;
	case SCTP_HOSTNAME_ADDRESS_PARAMETER_TYPE:
		result = sctp_hostname_parameter_to_string(s,
			(struct sctp_hostname_address_parameter *)parameter,
			error);
		break;
	case SCTP_SUPPORTED_ADDRESS_TYPES_PARAMETER_TYPE:
		result = sctp_supported_address_types_parameter_to_string(s,
			(struct sctp_supported_address_types_parameter *)parameter,
			error);
		break;
	case SCTP_OUTGOING_SSN_RESET_REQUEST_PARAMETER_TYPE:
		result = sctp_outgoing_ssn_reset_request_parameter_to_string(s,
			(struct sctp_outgoing_ssn_reset_request_parameter *)parameter, error);
		break;
	case SCTP_INCOMING_SSN_RESET_REQUEST_PARAMETER_TYPE:
		result = sctp_incoming_ssn_reset_request_parameter_to_string(s,
			(struct sctp_incoming_ssn_reset_request_parameter *)parameter, error);
	case SCTP_SSN_TSN_RESET_REQUEST_PARAMETER_TYPE:
		result = sctp_ssn_tsn_reset_request_parameter_to_string(s,
			(struct sctp_ssn_tsn_reset_request_parameter *)parameter, error);
		break;
	case SCTP_RECONFIG_RESPONSE_PARAMETER_TYPE:
		result = sctp_reconfig_response_parameter_to_string(s,
			(struct sctp_reconfig_response_parameter *)parameter, error);
		break;
	case SCTP_ADD_OUTGOING_STREAMS_REQUEST_PARAMETER_TYPE:
		result = sctp_add_outgoing_streams_request_parameter_to_string(s,
			(struct sctp_add_outgoing_streams_request_parameter *)parameter, error);
		break;
	case SCTP_ADD_INCOMING_STREAMS_REQUEST_PARAMETER_TYPE:
		result = sctp_add_incoming_streams_request_parameter_to_string(s,
			(struct sctp_add_incoming_streams_request_parameter *)parameter, error);
		break;
	case SCTP_ECN_CAPABLE_PARAMETER_TYPE:
		result = sctp_ecn_capable_parameter_to_string(s,
			(struct sctp_ecn_capable_parameter *)parameter, error);
		break;
	case SCTP_SUPPORTED_EXTENSIONS_PARAMETER_TYPE:
		result = sctp_supported_extensions_parameter_to_string(s,
			(struct sctp_supported_extensions_parameter *)parameter,
			error);
		break;
	case SCTP_PAD_PARAMETER_TYPE:
		result = sctp_pad_parameter_to_string(s,
			(struct sctp_pad_parameter *)parameter, error);
		break;
	case SCTP_ADAPTATION_INDICATION_PARAMETER_TYPE:
		result = sctp_adaptation_indication_parameter_to_string(s,
			(struct sctp_adaptation_indication_parameter *)parameter, error);
		break;
	case SCTP_FORWARD_TSN_SUPPORTED_PARAMETER_TYPE:
		result = sctp_forward_tsn_supported_parameter_to_string(s,
			(struct sctp_forward_tsn_supported_parameter *)parameter, error);
		break;
	default:
		result = sctp_unknown_parameter_to_string(s, parameter, error);
		break;
	}
	return result;
}

static int sctp_invalid_stream_identifier_cause_to_string(
	FILE *s,
	struct sctp_invalid_stream_identifier_cause *cause,
	char **error)
{
	u16 length;

	length = ntohs(cause->length);
	if (length != sizeof(struct sctp_invalid_stream_identifier_cause)) {
		asprintf(error,
			 "INVALID_STREAM_IDENTIFIER cause invalid (length=%u)",
			 length);
		return STATUS_ERR;
	}
	fprintf(s, "INVALID_STREAM_IDENTIFIER[sid=%u]", ntohs(cause->sid));
	return STATUS_OK;
}

static int sctp_missing_mandatory_parameter_cause_to_string(
	FILE *s,
	struct sctp_missing_mandatory_parameter_cause *cause,
	char **error)
{
	u16 length;
	u32 i, nr_parameters;

	length = ntohs(cause->length);
	if (length < sizeof(struct sctp_missing_mandatory_parameter_cause)) {
		asprintf(error,
			 "MISSING_MANDATORY_PARAMETER cause too short (length=%u)",
			 length);
		return STATUS_ERR;
	}
	nr_parameters = ntohl(cause->nr_parameters);
	if (length != sizeof(struct sctp_missing_mandatory_parameter_cause) +
		      nr_parameters * sizeof(u16)) {
		asprintf(error, "MISSING_MANDATORY_PARAMETER inconsistent");
		return STATUS_ERR;
	}
	fputs("MISSING_MANDATORY_PARAMETER[types=[", s);
	for (i = 0; i < nr_parameters; i++) {
		if (i > 0)
			fputs(", ", s);
		switch (ntohs(cause->parameter_type[i])) {
		case SCTP_IPV4_ADDRESS_PARAMETER_TYPE:
			fputs("IPV4_ADDRESS", s);
			break;
		case SCTP_IPV6_ADDRESS_PARAMETER_TYPE:
			fputs("IPV6_ADDRESS", s);
			break;
		case SCTP_STATE_COOKIE_PARAMETER_TYPE:
			fputs("STATE_COOKIE", s);
			break;
		case SCTP_UNRECOGNIZED_PARAMETER_PARAMETER_TYPE:
			fputs("UNRECOGNIZED_PARAMETER", s);
			break;
		case SCTP_COOKIE_PRESERVATIVE_PARAMETER_TYPE:
			fputs("COOKIE_PRESERVATIVE", s);
			break;
		case SCTP_HOSTNAME_ADDRESS_PARAMETER_TYPE:
			fputs("HOSTNAME_ADDRESS", s);
			break;
		case SCTP_SUPPORTED_ADDRESS_TYPES_PARAMETER_TYPE:
			fputs("SUPPORTED_ADDRESS_TYPES", s);
			break;
		case SCTP_ECN_CAPABLE_PARAMETER_TYPE:
			fputs("ECN_CAPABLE", s);
			break;
		default:
			fprintf(s, "0x%04x", ntohs(cause->parameter_type[i]));
			break;
		}
	}
	fputs("]]", s);
	return STATUS_OK;
}

static int sctp_stale_cookie_error_cause_to_string(
	FILE *s,
	struct sctp_stale_cookie_error_cause *cause,
	char **error)
{
	u16 length;

	length = ntohs(cause->length);
	if (length != sizeof(struct sctp_stale_cookie_error_cause)) {
		asprintf(error, "STALE_COOKIE_ERROR cause invalid (length=%u)",
			 length);
		return STATUS_ERR;
	}
	fprintf(s, "STALE_COOKIE_ERROR[staleness=%u]", ntohl(cause->staleness));
	return STATUS_OK;
}

static int sctp_out_of_resources_cause_to_string(
	FILE *s,
	struct sctp_out_of_resources_cause *cause,
	char **error)
{
	u16 length;

	length = ntohs(cause->length);
	if (length != sizeof(struct sctp_out_of_resources_cause)) {
		asprintf(error, "OUT_OF_RESOURCES cause invalid (length=%u)",
			 length);
		return STATUS_ERR;
	}
	fputs("OUT_OF_RESOURCES[]", s);
	return STATUS_OK;
}

static int sctp_unresolvable_address_cause_to_string(
	FILE *s,
	struct sctp_unresolvable_address_cause *cause,
	char **error)
{
	u16 cause_length, parameter_length, cause_padding, parameter_padding;
	struct sctp_parameter *parameter;
	int result;

	cause_length = ntohs(cause->length);
	if (cause_length < sizeof(struct sctp_unresolvable_address_cause) +
			   sizeof(struct sctp_parameter)) {
		asprintf(error, "UNRESOLVABLE_ADDRESS cause too short");
		return STATUS_ERR;
	}
	cause_padding = cause_length & 0x0003;
	if (cause_padding != 0)
		cause_padding = 4 - cause_padding;
	parameter = (struct sctp_parameter *)cause->parameter;
	parameter_length = ntohs(parameter->length);
	parameter_padding = parameter_length & 0x0003;
	if (parameter_padding != 0)
		parameter_padding = 4 - parameter_padding;
	if (cause_length + cause_padding !=
	    sizeof(struct sctp_unresolvable_address_cause) +
	    parameter_length + parameter_padding) {
		asprintf(error, "UNRESOLVABLE_ADDRESS cause inconsistent");
		return STATUS_ERR;
	}
	fputs("UNRESOLVABLE_ADDRESS[param=", s);
	result = sctp_parameter_to_string(s, parameter, error);
	fputc(']', s);
	return result;
}

static int sctp_unrecognized_chunk_type_cause_to_string(
	FILE *s,
	struct sctp_unrecognized_chunk_type_cause *cause,
	char **error)
{
	u16 cause_length, chunk_length, cause_padding, chunk_padding;
	struct sctp_chunk *chunk;
	int result;

	cause_length = ntohs(cause->length);
	if (cause_length < sizeof(struct sctp_unrecognized_chunk_type_cause) +
			   sizeof(struct sctp_chunk)) {
		asprintf(error, "UNRECOGNIZED_CHUNK_TYPE cause too short");
		return STATUS_ERR;
	}
	cause_padding = cause_length & 0x0003;
	if (cause_padding != 0)
		cause_padding = 4 - cause_padding;
	chunk = (struct sctp_chunk *)cause->chunk;
	chunk_length = ntohs(chunk->length);
	chunk_padding = chunk_length & 0x0003;
	if (chunk_padding != 0)
		chunk_padding = 4 - chunk_padding;
	/* XXX: Do we need to deal with padding here? */
	if (cause_length + cause_padding !=
	    sizeof(struct sctp_unrecognized_chunk_type_cause) +
	    chunk_length + chunk_padding) {
		asprintf(error, "UNRECOGNIZED_CHUNK_TYPE cause inconsistent");
	fputs("UNRECOGNIZED_CHUNK_TYPE[chk=", s);
	result = sctp_chunk_to_string(s, chunk, error);
	fputc(']', s);
	return result;
}

static int sctp_invalid_mandatory_parameter_cause_to_string(
	FILE *s,
	struct sctp_invalid_mandatory_parameter_cause *cause,
	char **error)
{
	u16 length;

	length = ntohs(cause->length);
	if (length != sizeof(struct sctp_invalid_mandatory_parameter_cause)) {
		asprintf(error,
			 "INVALID_MANDATORY_PARAMETER cause invalid (length=%u)",
			 length);
		return STATUS_ERR;
	}
	fputs("INVALID_MANDATORY_PARAMETER[]", s);
	return STATUS_OK;
}

static int sctp_unrecognized_parameters_cause_to_string(
	FILE *s,
	struct sctp_unrecognized_parameters_cause *cause,
	char **error)
{
	u16 length, parameters_length, index;
	struct sctp_parameters_iterator iter;
	struct sctp_parameter *parameter;
	int result = STATUS_OK;

	length = ntohs(cause->length);
	if (length < sizeof(struct sctp_unrecognized_parameters_cause)) {
		asprintf(error,
			 "UNRECOGNIZED_PARAMETERS cause too short (length=%u)",
			 length);
		return STATUS_ERR;
	}
	parameters_length = length -
			    sizeof(struct sctp_unrecognized_parameters_cause);
	fputs("UNRECOGNIZED_PARAMETERS[", s);
	index = 0;
	for (parameter = sctp_parameters_begin(cause->parameters,
					       parameters_length,
					       &iter, error);
	     parameter != NULL;
	     parameter = sctp_parameters_next(&iter, error)) {
		if (index > 0)
			fputs(", ", s);
		if (*error != NULL) {
			fputs(*error, s);
			free(*error);
			*error = NULL;
		result = sctp_parameter_to_string(s, parameter, error);
		if (result != STATUS_OK)
			break;
		index++;
	}
	fputc(']', s);
	return STATUS_OK;
}

static int sctp_no_user_data_cause_to_string(
	FILE *s,
	struct sctp_no_user_data_cause *cause,
	char **error)
{
	u16 length;

	length = ntohs(cause->length);
	if (length != sizeof(struct sctp_no_user_data_cause)) {
		asprintf(error, "NO_USER_DATA cause invalid (length=%u)",
			 length);
		return STATUS_ERR;
	}
	fprintf(s, "NO_USER_DATA[tsn=%u]", ntohl(cause->tsn));
	return STATUS_OK;
}

static int sctp_cookie_received_while_shutdown_cause_to_string(
	FILE *s,
	struct sctp_cookie_received_while_shutdown_cause *cause,
	char **error)
{
	u16 length;

	length = ntohs(cause->length);
	if (length !=
	    sizeof(struct sctp_cookie_received_while_shutdown_cause)) {
		asprintf(error,
			 "COOKIE_RECEIVED_WHILE_SHUTDOWN cause invalid (length=%u)",
			 length);
		return STATUS_ERR;
	}
	fputs("COOKIE_RECEIVED_WHILE_SHUTDOWN[]", s);
	return STATUS_OK;
}

static int sctp_restart_with_new_addresses_cause_to_string(
	FILE *s,
	struct sctp_restart_with_new_addresses_cause *cause,
	char **error)
{
	u16 length, addressess_length, index;
	struct sctp_parameters_iterator iter;
	struct sctp_parameter *parameter;
	int result = STATUS_OK;

	length = ntohs(cause->length);
	if (length < sizeof(struct sctp_restart_with_new_addresses_cause)) {
		asprintf(error,
			 "RESTART_WITH_NEW_ADDRESSES cause too short (length=%u)",
			 length);
		return STATUS_ERR;
	}
	addressess_length =
		length - sizeof(struct sctp_restart_with_new_addresses_cause);
	fputs("RESTART_WITH_NEW_ADDRESSES[", s);
	index = 0;
	for (parameter = sctp_parameters_begin(cause->addresses,
					       addressess_length,
					       &iter, error);
	     parameter != NULL;
	     parameter = sctp_parameters_next(&iter, error)) {
		if (index > 0)
			fputs(", ", s);
		if (*error != NULL) {
			fputs(*error, s);
			free(*error);
			*error = NULL;
		result = sctp_parameter_to_string(s, parameter, error);
		if (result != STATUS_OK)
			break;
		index++;
	}
	fputc(']', s);
	return STATUS_OK;
}

static int sctp_user_initiated_abort_cause_to_string(
	FILE *s,