Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
sctp_chunk_to_string.c 39.85 KiB
/*
 * 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)
{
	u16 length;

	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)",
			 length);
		return STATUS_ERR;
	}
	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);
	return STATUS_OK;
}

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_I_DATA_CHUNK_TYPE:
			fputs("I-DATA", s);
			break;
		case SCTP_PAD_CHUNK_TYPE:
			fputs("PAD", 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_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;
	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("ADAPTATION_INDICATION[", s);
	fprintf(s, "type=0x%04x, ", ntohs(parameter->type));
	fprintf(s, "len=%hu, ", ntohs(parameter->length));
	fprintf(s, "val=%u", parameter->adaptation_code_point);
	fputs("]", s);
	return STATUS_OK;
}

static int sctp_parameter_to_string(FILE *s,
				    struct sctp_parameter *parameter,
				    char **error)
{
	int result;
	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_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;
	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 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 cause inconsistent");
		return STATUS_ERR;
	}
	fputs("UNRECOGNIZED_CHUNK[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)
			break;
		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)
			break;
		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,
	struct sctp_user_initiated_abort_cause *cause,
	char **error)
{
	u16 length;

	length = ntohs(cause->length);
	if (length < sizeof(struct sctp_user_initiated_abort_cause)) {
		asprintf(error,
			 "USER_INITIATED_ABORT cause illegal (length=%u)",
			 length);
		return STATUS_ERR;
	}
	fprintf(s, "USER_INITIATED_ABORT[info=\"%.*s\"]",
		(int)(length - sizeof(struct sctp_user_initiated_abort_cause)),
		(char *)cause->information);
	return STATUS_OK;
}

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

	length = ntohs(cause->length);
	if (length < sizeof(struct sctp_protocol_violation_cause)) {
		asprintf(error, "PROTOCOL_VIOLOATION cause illegal (length=%u)",
			 length);
		return STATUS_ERR;
	}
	fprintf(s, "PROTOCOL_VIOLATION[info=\"%.*s\"]",
		(int)(length - sizeof(struct sctp_protocol_violation_cause)),
		(char *)cause->information);
	return STATUS_OK;
}

static int sctp_unknown_cause_to_string(FILE *s,
					struct sctp_cause *cause,
					char **error)
{
	u16 i, length;

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

static int sctp_cause_to_string(FILE *s, struct sctp_cause *cause, char **error)
{
	int result;

	switch (ntohs(cause->code)) {
	case SCTP_INVALID_STREAM_IDENTIFIER_CAUSE_CODE:
		result = sctp_invalid_stream_identifier_cause_to_string(s,
			(struct sctp_invalid_stream_identifier_cause *)cause,
			error);
		break;
	case SCTP_MISSING_MANDATORY_PARAMETER_CAUSE_CODE:
		result = sctp_missing_mandatory_parameter_cause_to_string(s,
			(struct sctp_missing_mandatory_parameter_cause *)cause,
			error);
		break;
	case SCTP_STALE_COOKIE_ERROR_CAUSE_CODE:
		result = sctp_stale_cookie_error_cause_to_string(s,
			(struct sctp_stale_cookie_error_cause *)cause, error);
		break;
	case SCTP_OUT_OF_RESOURCES_CAUSE_CODE:
		result = sctp_out_of_resources_cause_to_string(s,
			(struct sctp_out_of_resources_cause *)cause, error);
		break;
	case SCTP_UNRESOLVABLE_ADDRESS_CAUSE_CODE:
		result = sctp_unresolvable_address_cause_to_string(s,
			(struct sctp_unresolvable_address_cause *)cause, error);
		break;
	case SCTP_UNRECOGNIZED_CHUNK_TYPE_CAUSE_CODE:
		result = sctp_unrecognized_chunk_type_cause_to_string(s,
			(struct sctp_unrecognized_chunk_type_cause *)cause,
			error);
		break;
	case SCTP_INVALID_MANDATORY_PARAMETER_CAUSE_CODE:
		result = sctp_invalid_mandatory_parameter_cause_to_string(s,
			(struct sctp_invalid_mandatory_parameter_cause *)cause,
			error);
		break;
	case SCTP_UNRECOGNIZED_PARAMETERS_CAUSE_CODE:
		result = sctp_unrecognized_parameters_cause_to_string(s,
			(struct sctp_unrecognized_parameters_cause *)cause,
			error);
		break;
	case SCTP_NO_USER_DATA_CAUSE_CODE:
		result = sctp_no_user_data_cause_to_string(s,
			(struct sctp_no_user_data_cause *)cause, error);
		break;
	case SCTP_COOKIE_RECEIVED_WHILE_SHUTDOWN_CAUSE_CODE:
		result = sctp_cookie_received_while_shutdown_cause_to_string(s,
			(struct sctp_cookie_received_while_shutdown_cause *)cause,
			error);
		break;
	case SCTP_RESTART_WITH_NEW_ADDRESSES_CAUSE_CODE:
		result = sctp_restart_with_new_addresses_cause_to_string(s,
			(struct sctp_restart_with_new_addresses_cause *)cause,
			error);
		break;
	case SCTP_USER_INITIATED_ABORT_CAUSE_CODE:
		result = sctp_user_initiated_abort_cause_to_string(s,
			(struct sctp_user_initiated_abort_cause *)cause, error);
		break;
	case SCTP_PROTOCOL_VIOLATION_CAUSE_CODE:
		result = sctp_protocol_violation_cause_to_string(s,
			(struct sctp_protocol_violation_cause *)cause, error);
		break;
	default:
		result = sctp_unknown_cause_to_string(s, cause, error);
		break;
	}
	return result;
}

static int sctp_data_chunk_to_string(FILE *s,
				     struct sctp_data_chunk *chunk,
				     char **error)
{
	u16 length;
	u8 flags;

	flags = chunk->flags;
	length = ntohs(chunk->length);
	if (length < sizeof(struct sctp_data_chunk)) {
		asprintf(error, "DATA chunk too short (length=%u)", length);
		return STATUS_ERR;
	}
	fputs("DATA[", s);
	fputs("flgs=", s);
	if ((flags & ~(SCTP_DATA_CHUNK_I_BIT |
		       SCTP_DATA_CHUNK_U_BIT |
		       SCTP_DATA_CHUNK_B_BIT |
		       SCTP_DATA_CHUNK_E_BIT)) || (flags == 0x00))
		fprintf(s, "0x%02x", chunk->flags);
	else {
		if (flags & SCTP_DATA_CHUNK_I_BIT)
			fputc('I', s);
		if (flags & SCTP_DATA_CHUNK_U_BIT)
			fputc('U', s);
		if (flags & SCTP_DATA_CHUNK_B_BIT)
			fputc('B', s);
		if (flags & SCTP_DATA_CHUNK_E_BIT)
			fputc('E', s);
	}
	fputs(", ", s);
	fprintf(s, "len=%u, ", length);
	fprintf(s, "tsn=%u, ", ntohl(chunk->tsn));
	fprintf(s, "sid=%d, ", ntohs(chunk->sid));
	fprintf(s, "ssn=%u, ", ntohs(chunk->ssn));
	fprintf(s, "ppid=%u]", ntohl(chunk->ppid));
	return STATUS_OK;
}

static int sctp_init_chunk_to_string(FILE *s,
				     struct sctp_init_chunk *chunk,
				     char **error)
{
	struct sctp_parameters_iterator iter;
	struct sctp_parameter *parameter;
	u16 length, parameters_length;
	int result = STATUS_OK;

	assert(*error == NULL);
	length = ntohs(chunk->length);
	if (length < sizeof(struct sctp_init_chunk)) {
		asprintf(error, "INIT chunk too short (length=%u)", length);
		return STATUS_ERR;
	}
	parameters_length = length - sizeof(struct sctp_init_chunk);
	fputs("INIT[", s);
	fprintf(s, "flgs=0x%02x, ", chunk->flags);
	fprintf(s, "tag=%u, ", ntohl(chunk->initiate_tag));
	fprintf(s, "a_rwnd=%d, ", ntohl(chunk->a_rwnd));
	fprintf(s, "os=%u, ", ntohs(chunk->os));
	fprintf(s, "is=%u, ", ntohs(chunk->is));
	fprintf(s, "tsn=%u", ntohl(chunk->initial_tsn));
	for (parameter = sctp_parameters_begin(chunk->parameter,
					       parameters_length,
					       &iter, error);
	     parameter != NULL;
	     parameter = sctp_parameters_next(&iter, error)) {
		fputs(", ", s);
		if (*error != NULL)
			break;
		result = sctp_parameter_to_string(s, parameter, error);
		if (result != STATUS_OK)
			break;
	}
	fputc(']', s);
	if (*error != NULL)
		result = STATUS_ERR;
	return result;
}

static int sctp_init_ack_chunk_to_string(FILE *s,
					 struct sctp_init_ack_chunk *chunk,
					 char **error)
{
	struct sctp_parameters_iterator iter;
	struct sctp_parameter *parameter;
	u16 length, parameters_length;
	int result = STATUS_OK;

	assert(*error == NULL);
	length = ntohs(chunk->length);
	if (length < sizeof(struct sctp_init_ack_chunk)) {
		asprintf(error, "INIT_ACK chunk too short (length=%u)", length);
		return STATUS_ERR;
	}
	parameters_length = length - sizeof(struct sctp_init_ack_chunk);
	fputs("INIT_ACK[", s);
	fprintf(s, "flgs=0x%02x, ", chunk->flags);
	fprintf(s, "tag=%u, ", ntohl(chunk->initiate_tag));
	fprintf(s, "a_rwnd=%d, ", ntohl(chunk->a_rwnd));
	fprintf(s, "os=%u, ", ntohs(chunk->os));
	fprintf(s, "is=%u, ", ntohs(chunk->is));
	fprintf(s, "tsn=%u", ntohl(chunk->initial_tsn));
	for (parameter = sctp_parameters_begin(chunk->parameter,
					       parameters_length,
					       &iter, error);
	     parameter != NULL;
	     parameter = sctp_parameters_next(&iter, error)) {
		fputs(", ", s);
		if (*error != NULL)
			break;
		result = sctp_parameter_to_string(s, parameter, error);
		if (result != STATUS_OK)
			break;
	}
	fputc(']', s);
	if (*error != NULL)
		result = STATUS_ERR;
	return result;
}

static int sctp_sack_chunk_to_string(FILE *s,
				     struct sctp_sack_chunk *chunk,
				     char **error)
{
	u16 length;
	u16 nr_gaps, nr_dups;
	u16 i;

	length = ntohs(chunk->length);
	if (length < sizeof(struct sctp_sack_chunk)) {
		asprintf(error, "SACK chunk too short (length=%u)", length);
		return STATUS_ERR;
	}
	nr_gaps = ntohs(chunk->nr_gap_blocks);
	nr_dups = ntohs(chunk->nr_dup_tsns);
	if (length != sizeof(struct sctp_sack_chunk) +
		      (nr_gaps + nr_dups) * sizeof(u32)) {
		asprintf(error, "SACK chunk length inconsistent");
		return STATUS_ERR;
	}
	fputs("SACK[", s);
	fprintf(s, "flgs=0x%02x, ", chunk->flags);
	fprintf(s, "cum_tsn=%u, ", ntohl(chunk->cum_tsn));
	fprintf(s, "a_rwnd=%u, ", ntohl(chunk->a_rwnd));
	fputs("gaps=[", s);
	for (i = 0; i < nr_gaps; i++)
		fprintf(s, "%s%u:%u",
			   i > 0 ? ", " : "",
			   ntohs(chunk->block[i].gap.start),
			   ntohs(chunk->block[i].gap.end));
	fputs("], dups=[", s);
	for (i = 0; i < nr_dups; i++)
		fprintf(s, "%s%u",
			   i > 0 ? ", " : "",
			   ntohl(chunk->block[i + nr_gaps].tsn));
	fputs("]]", s);
	return STATUS_OK;
}

static int sctp_heartbeat_chunk_to_string(FILE *s,
					  struct sctp_heartbeat_chunk *chunk,
					  char **error)
{
	u16 chunk_length, parameter_length, chunk_padding, parameter_padding;
	struct sctp_parameter *parameter;
	int result;

	chunk_length = ntohs(chunk->length);
	if (chunk_length < sizeof(struct sctp_heartbeat_chunk) +
	                   sizeof(struct sctp_heartbeat_information_parameter)) {
		asprintf(error, "HEARTBEAT chunk too short");
		return STATUS_ERR;
	}
	chunk_padding = chunk_length & 0x0003;
	if (chunk_padding != 0)
		chunk_padding = 4 - chunk_padding;
	parameter = (struct sctp_parameter *)chunk->value;
	parameter_length = ntohs(parameter->length);
	parameter_padding = parameter_length & 0x0003;
	if (parameter_padding != 0)
		parameter_padding = 4 - parameter_padding;
	if (chunk_length + chunk_padding !=
	    sizeof(struct sctp_heartbeat_chunk) +
	    parameter_length + parameter_padding) {
		asprintf(error, "HEARTBEAT chunk inconsistent");
		return STATUS_ERR;
	}
	fputs("HEARTBEAT[", s);
	fprintf(s, "flgs=0x%02x, ", chunk->flags);
	result = sctp_parameter_to_string(s, parameter, error);
	fputc(']', s);
	return result;
}

static int sctp_heartbeat_ack_chunk_to_string(
	FILE *s,
	struct sctp_heartbeat_ack_chunk *chunk,
	char **error)
{
	u16 chunk_length, parameter_length, chunk_padding, parameter_padding;
	struct sctp_parameter *parameter;
	int result;

	chunk_length = ntohs(chunk->length);
	if (chunk_length < sizeof(struct sctp_heartbeat_ack_chunk) +
	                   sizeof(struct sctp_heartbeat_information_parameter)) {
		asprintf(error, "HEARTBEAT_ACK chunk too short");
		return STATUS_ERR;
	}
	chunk_padding = chunk_length & 0x0003;
	if (chunk_padding != 0)
		chunk_padding = 4 - chunk_padding;
	parameter = (struct sctp_parameter *)chunk->value;
	parameter_length = ntohs(parameter->length);
	parameter_padding = parameter_length & 0x0003;
	if (parameter_padding != 0)
		parameter_padding = 4 - parameter_padding;
	if (chunk_length + chunk_padding !=
	    sizeof(struct sctp_heartbeat_chunk) +
	    parameter_length + parameter_padding) {
		asprintf(error, "HEARTBEAT_ACK chunk inconsistent");
		return STATUS_ERR;
	}
	fputs("HEARTBEAT_ACK[", s);
	fprintf(s, "flgs=0x%02x, ", chunk->flags);
	result = sctp_parameter_to_string(s, parameter, error);
	fputc(']', s);
	return result;
}

static int sctp_abort_chunk_to_string(FILE *s,
				      struct sctp_abort_chunk *chunk,
				      char **error)
{
	struct sctp_causes_iterator iter;
	struct sctp_cause *cause;
	u16 length, index;
	u8 flags;
	int result = STATUS_OK;

	flags = chunk->flags;
	length = ntohs(chunk->length);
	if (length < sizeof(struct sctp_abort_chunk)) {
		asprintf(error, "ABORT chunk too short (length=%u)", length);
		return STATUS_ERR;
	}
	fputs("ABORT[", s);
	fputs("flgs=", s);
	if ((flags & ~SCTP_ABORT_CHUNK_T_BIT) || (flags == 0x00))
		fprintf(s, "0x%02x", flags);
	else
		if (flags & SCTP_ABORT_CHUNK_T_BIT)
			fputc('T', s);
	index = 0;
	for (cause = sctp_causes_begin((struct sctp_chunk *)chunk,
				       SCTP_ABORT_CHUNK_CAUSE_OFFSET,
				       &iter, error);
	     cause != NULL;
	     cause = sctp_causes_next(&iter, error)) {
		fputs(", ", s);
		if (*error != NULL)
			break;
		result = sctp_cause_to_string(s, cause, error);
		if (result != STATUS_OK)
			break;
		index++;
	}
	fputc(']', s);
	if (*error != NULL)
		result = STATUS_ERR;
	return result;
}

static int sctp_shutdown_chunk_to_string(FILE *s,
					 struct sctp_shutdown_chunk *chunk,
					 char **error)
{
	u16 length;

	length = ntohs(chunk->length);
	if (length != sizeof(struct sctp_shutdown_chunk)) {
		asprintf(error, "SHUTDOWN chunk illegal (length=%u)", length);
		return STATUS_ERR;
	}
	fputs("SHUTDOWN[", s);
	fprintf(s, "flgs=0x%02x, ", chunk->flags);
	fprintf(s, "cum_tsn=%u", ntohl(chunk->cum_tsn));
	fputc(']', s);
	return STATUS_OK;
}

static int sctp_shutdown_ack_chunk_to_string(
	FILE *s,
	struct sctp_shutdown_ack_chunk *chunk,
	char **error)
{
	u16 length;

	length = ntohs(chunk->length);
	if (length != sizeof(struct sctp_shutdown_ack_chunk)) {
		asprintf(error, "SHUTDOWN_ACK chunk too long (length=%u)",
			 length);
		return STATUS_ERR;
	}
	fputs("SHUTDOWN_ACK[", s);
	fprintf(s, "flgs=0x%02x", chunk->flags);
	fputc(']', s);
	return STATUS_OK;
}

static int sctp_error_chunk_to_string(FILE *s,
				      struct sctp_error_chunk *chunk,
				      char **error)
{
	struct sctp_causes_iterator iter;
	struct sctp_cause *cause;
	u16 length, index;
	int result = STATUS_OK;

	length = ntohs(chunk->length);
	if (length < sizeof(struct sctp_abort_chunk)) {
		asprintf(error, "ERROR chunk too short (length=%u)", length);
		return STATUS_ERR;
	}
	fputs("ERROR[", s);
	fprintf(s, "flgs=0x%02x", chunk->flags);
	index = 0;
	for (cause = sctp_causes_begin((struct sctp_chunk *)chunk,
				       SCTP_ERROR_CHUNK_CAUSE_OFFSET,
				       &iter, error);
	     cause != NULL;
	     cause = sctp_causes_next(&iter, error)) {
		fputs(", ", s);
		if (*error != NULL)
			break;
		result = sctp_cause_to_string(s, cause, error);
		if (result != STATUS_OK)
			break;
		index++;
	}
	fputc(']', s);
	if (*error != NULL)
		result = STATUS_ERR;
	return STATUS_OK;
}

static int sctp_cookie_echo_chunk_to_string(
	FILE *s,
	struct sctp_cookie_echo_chunk *chunk,
	char **error)
{
	u16 length;

	length = ntohs(chunk->length);
	fputs("COOKIE_ECHO[", s);
	fprintf(s, "flgs=0x%02x, ", chunk->flags);
	fprintf(s, "len=%u", length);
	fputc(']', s);
	return STATUS_OK;
}

static int sctp_cookie_ack_chunk_to_string(FILE *s,
					   struct sctp_cookie_ack_chunk *chunk,
					   char **error)
{
	u16 length;

	length = ntohs(chunk->length);
	if (length != sizeof(struct sctp_cookie_ack_chunk)) {
		asprintf(error, "COOKIE_ACK chunk too long (length=%u)",
			 length);
		return STATUS_ERR;
	}
	fputs("COOKIE_ACK[", s);
	fprintf(s, "flgs=0x%02x", chunk->flags);
	fputc(']', s);
	return STATUS_OK;
}

static int sctp_ecne_chunk_to_string(FILE *s,
				     struct sctp_ecne_chunk *chunk,
				     char **error)
{
	u16 length;
	length = ntohs(chunk->length);
	if (length != sizeof(struct sctp_ecne_chunk)) {
		asprintf(error, "ECNE chunk illegal (length=%u)", length);
		return STATUS_ERR;
	}
	fputs("ECNE[", s);
	fprintf(s, "flgs=0x%02x, ", chunk->flags);
	fprintf(s, "tsn=%u", ntohl(chunk->lowest_tsn));
	fputc(']', s);
	return STATUS_OK;
}

static int sctp_cwr_chunk_to_string(FILE *s,
				    struct sctp_cwr_chunk *chunk,
				    char **error)
{
	u16 length;

	length = ntohs(chunk->length);
	if (length != sizeof(struct sctp_cwr_chunk)) {
		asprintf(error, "CWR chunk illegal (length=%u)", length);
		return STATUS_ERR;
	}
	fputs("CWR[", s);
	fprintf(s, "flgs=0x%02x, ", chunk->flags);
	fprintf(s, "tsn=%u", ntohl(chunk->lowest_tsn));
	fputc(']', s);
	return STATUS_OK;
}

static int sctp_shutdown_complete_chunk_to_string(
	FILE *s,
	struct sctp_shutdown_complete_chunk *chunk,
	char **error)
{
	u8 flags;
	u16 length;

	flags = chunk->flags;
	length = ntohs(chunk->length);
	if (length != sizeof(struct sctp_shutdown_complete_chunk)) {
		asprintf(error, "SHUTDOWN_COMPLETE chunk too long (length=%u)",
			 length);
		return STATUS_ERR;
	}
	fputs("SHUTDOWN_COMPLETE[", s);
	fputs("flgs=", s);
	if ((flags & ~SCTP_SHUTDOWN_COMPLETE_CHUNK_T_BIT) || (flags == 0x00))
		fprintf(s, "0x%02x", flags);
	else
		if (flags & SCTP_SHUTDOWN_COMPLETE_CHUNK_T_BIT)
			fputc('T', s);
	fputc(']', s);
	return STATUS_OK;
}

static int sctp_i_data_chunk_to_string(FILE *s,
				       struct sctp_i_data_chunk *chunk,
				       char **error)
{
	u16 length;
	u8 flags;

	flags = chunk->flags;
	length = ntohs(chunk->length);
	if (length < sizeof(struct sctp_i_data_chunk)) {
		asprintf(error, "I-DATA chunk too short (length=%u)", length);
		return STATUS_ERR;
	}
	fputs("I-DATA[", s);
	fputs("flgs=", s);
	if ((flags & ~(SCTP_I_DATA_CHUNK_I_BIT |
		       SCTP_I_DATA_CHUNK_U_BIT |
		       SCTP_I_DATA_CHUNK_B_BIT |
		       SCTP_I_DATA_CHUNK_E_BIT)) || (flags == 0x00))
		fprintf(s, "0x%02x", chunk->flags);
	else {
		if (flags & SCTP_I_DATA_CHUNK_I_BIT)
			fputc('I', s);
		if (flags & SCTP_I_DATA_CHUNK_U_BIT)
			fputc('U', s);
		if (flags & SCTP_I_DATA_CHUNK_B_BIT)
			fputc('B', s);
		if (flags & SCTP_I_DATA_CHUNK_E_BIT)
			fputc('E', s);
	}
	fputs(", ", s);
	fprintf(s, "len=%u, ", length);
	fprintf(s, "tsn=%u, ", ntohl(chunk->tsn));
	fprintf(s, "sid=%d, ", ntohs(chunk->sid));
	fprintf(s, "mid=%u, ", ntohl(chunk->mid));
	if (flags & SCTP_I_DATA_CHUNK_B_BIT)
		fprintf(s, "ppid=%u", ntohl(chunk->field.ppid));
	else
		fprintf(s, "fsn=%u", ntohl(chunk->field.fsn));
	fputc(']', s);
	return STATUS_OK;
}

static int sctp_pad_chunk_to_string(
	FILE *s,
	struct sctp_pad_chunk *chunk,
	char **error)
{
	u16 length;

	length = ntohs(chunk->length);
	fputs("PAD[", s);
	fprintf(s, "flgs=0x%02x, ", chunk->flags);
	fprintf(s, "len=%u, ", length);
	fputs("val=...]", s);
	return STATUS_OK;
}

static int sctp_unknown_chunk_to_string(FILE *s,
					struct sctp_chunk *chunk,
					char **error)
{
	u16 i, length;

	length = ntohs(chunk->length);
	fputs("CHUNK[", s);
	fprintf(s, "type=0x%02x, ", chunk->type);
	fprintf(s, "flgs=0x%02x, ", chunk->flags);
	fputs("value=[", s);
	for (i = 0; i < length - sizeof(struct sctp_chunk); i++)
		fprintf(s, "%s0x%02x",
			   i > 0 ? ", " : "",
			   chunk->value[i]);
	fputs("]]", s);
	return STATUS_OK;
}

int sctp_chunk_to_string(FILE *s, struct sctp_chunk *chunk, char **error)
{
	int result;

	switch (chunk->type) {
	case SCTP_DATA_CHUNK_TYPE:
		result = sctp_data_chunk_to_string(s,
			(struct sctp_data_chunk *)chunk, error);
		break;
	case SCTP_INIT_CHUNK_TYPE:
		result = sctp_init_chunk_to_string(s,
			(struct sctp_init_chunk *)chunk, error);
		break;
	case SCTP_INIT_ACK_CHUNK_TYPE:
		result = sctp_init_ack_chunk_to_string(s,
			(struct sctp_init_ack_chunk *)chunk, error);
		break;
	case SCTP_SACK_CHUNK_TYPE:
		result = sctp_sack_chunk_to_string(s,
			(struct sctp_sack_chunk *)chunk, error);
		break;
	case SCTP_HEARTBEAT_CHUNK_TYPE:
		result = sctp_heartbeat_chunk_to_string(s,
			(struct sctp_heartbeat_chunk *)chunk, error);
		break;
	case SCTP_HEARTBEAT_ACK_CHUNK_TYPE:
		result = sctp_heartbeat_ack_chunk_to_string(s,
			(struct sctp_heartbeat_ack_chunk *)chunk, error);
		break;
	case SCTP_ABORT_CHUNK_TYPE:
		result = sctp_abort_chunk_to_string(s,
			(struct sctp_abort_chunk *)chunk, error);
		break;
	case SCTP_SHUTDOWN_CHUNK_TYPE:
		result = sctp_shutdown_chunk_to_string(s,
			(struct sctp_shutdown_chunk *)chunk, error);
		break;
	case SCTP_SHUTDOWN_ACK_CHUNK_TYPE:
		result = sctp_shutdown_ack_chunk_to_string(s,
			(struct sctp_shutdown_ack_chunk *)chunk, error);
		break;
	case SCTP_ERROR_CHUNK_TYPE:
		result = sctp_error_chunk_to_string(s,
			(struct sctp_error_chunk *)chunk, error);
		break;
	case SCTP_COOKIE_ECHO_CHUNK_TYPE:
		result = sctp_cookie_echo_chunk_to_string(s,
			(struct sctp_cookie_echo_chunk *)chunk, error);
		break;
	case SCTP_COOKIE_ACK_CHUNK_TYPE:
		result = sctp_cookie_ack_chunk_to_string(s,
			(struct sctp_cookie_ack_chunk *)chunk, error);
		break;
	case SCTP_ECNE_CHUNK_TYPE:
		result = sctp_ecne_chunk_to_string(s,
			(struct sctp_ecne_chunk *)chunk, error);
		break;
	case SCTP_CWR_CHUNK_TYPE:
		result = sctp_cwr_chunk_to_string(s,
			(struct sctp_cwr_chunk *)chunk, error);
		break;
	case SCTP_SHUTDOWN_COMPLETE_CHUNK_TYPE:
		result = sctp_shutdown_complete_chunk_to_string(s,
			(struct sctp_shutdown_complete_chunk *)chunk, error);
		break;
	case SCTP_I_DATA_CHUNK_TYPE:
		result = sctp_i_data_chunk_to_string(s,
			(struct sctp_i_data_chunk *)chunk, error);
		break;
	case SCTP_PAD_CHUNK_TYPE:
		result = sctp_pad_chunk_to_string(s,
			(struct sctp_pad_chunk *)chunk, error);
		break;
	default:
		result = sctp_unknown_chunk_to_string(s, chunk, error);
		break;
	}
	return result;
}