/*
 * RFC2367 PF_KEYv2 Key management API message parser
 * Copyright (C) 2003 Michael Richardson <mcr@freeswan.org>
 *
 * 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.  See <http://www.fsf.org/copyleft/gpl.txt>.
 *
 * 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.
 *
 */

#include <sys/types.h>
#include <stdio.h>

#include <libreswan.h>
#include <libreswan/pfkeyv2.h>
#include <libreswan/pfkey.h>

void pfkey_print(struct sadb_msg *msg, FILE *out)
{
	int len;
	struct sadb_ext *se;

	fprintf(out,
		"version=%d type=%d errno=%d satype=%d len=%d seq=%d pid=%d ",
		msg->sadb_msg_version,
		msg->sadb_msg_type,
		msg->sadb_msg_errno,
		msg->sadb_msg_satype,
		msg->sadb_msg_len,
		(int)msg->sadb_msg_seq,
		(int)msg->sadb_msg_pid);

	len = IPSEC_PFKEYv2_LEN(msg->sadb_msg_len);
	len -= sizeof(struct sadb_msg);

	se = (struct sadb_ext *)(&msg[1]);
	while (len > (int)sizeof(struct sadb_ext)) {
		fprintf(out, "{ext=%d len=%d ", se->sadb_ext_type,
			se->sadb_ext_len);

		/* make sure that there is enough left */
		if ((int)IPSEC_PFKEYv2_LEN(se->sadb_ext_len) > len) {
			fprintf(out, "short-packet(%d<%d) ", len,
				(int)IPSEC_PFKEYv2_LEN(se->sadb_ext_len));

			/* force it to match */
			se->sadb_ext_len = IPSEC_PFKEYv2_WORDS(len);
			goto dumpbytes;
		}

		/* okay, decode what we know */
		switch (se->sadb_ext_type) {
		case SADB_EXT_SA:
		{
			struct k_sadb_sa *sa = (struct k_sadb_sa *)se;
			fprintf(out,
				"spi=%08x replay=%d state=%d auth=%d encrypt=%d flags=%08x ref=%08x}",
				(int)sa->sadb_sa_spi,
				sa->sadb_sa_replay,
				sa->sadb_sa_state,
				sa->sadb_sa_auth,
				sa->sadb_sa_encrypt,
				(int)sa->sadb_sa_flags,
				(int)sa->sadb_x_sa_ref);
		}
		break;

		case SADB_X_EXT_ADDRESS_SRC_FLOW:
		case SADB_X_EXT_ADDRESS_DST_FLOW:
		case SADB_X_EXT_ADDRESS_SRC_MASK:
		case SADB_X_EXT_ADDRESS_DST_MASK:
		case SADB_EXT_ADDRESS_DST:
		case SADB_EXT_ADDRESS_SRC:
		{
			struct sadb_address *addr = (struct sadb_address *)se;
			int alen = IPSEC_PFKEYv2_LEN(addr->sadb_address_len) -
				   sizeof(struct sadb_address);
			unsigned char *bytes = (unsigned char *)&addr[1];

			fprintf(out, "proto=%d prefixlen=%d addr=0x",
				addr->sadb_address_proto,
				addr->sadb_address_prefixlen);

			while (alen > 0) {
				fprintf(out, "%02x", *bytes);
				bytes++;
				alen--;
			}
			fprintf(out, " } ");
		}
		break;

		case SADB_X_EXT_PROTOCOL:
		{
			struct sadb_protocol *sp = (struct sadb_protocol *)se;
			fprintf(out, "proto=%d direction=%d flags=%d } ",
				sp->sadb_protocol_proto,
				sp->sadb_protocol_direction,
				sp->sadb_protocol_flags);
		}
		break;

		case SADB_EXT_LIFETIME_CURRENT:
		case SADB_EXT_LIFETIME_HARD:
		case SADB_EXT_LIFETIME_SOFT:
		{
			struct sadb_lifetime *life =
				(struct sadb_lifetime *)se;

			fprintf(out,
				"allocations=%d bytes=%qd addtime=%qd usetime=%qd packets=%d",
				(int)life->sadb_lifetime_allocations,
				(long long)life->sadb_lifetime_bytes,
				(long long)life->sadb_lifetime_addtime,
				(long long)life->sadb_lifetime_usetime,
				(int)life->sadb_x_lifetime_packets);
			fprintf(out, " } ");
		}
		break;

		case SADB_EXT_RESERVED:
		case SADB_EXT_ADDRESS_PROXY:
		case SADB_EXT_KEY_AUTH:
		case SADB_EXT_KEY_ENCRYPT:
		case SADB_EXT_IDENTITY_SRC:
		case SADB_EXT_IDENTITY_DST:
		case SADB_EXT_SENSITIVITY:
		case SADB_EXT_PROPOSAL:
		case SADB_EXT_SUPPORTED_AUTH:
		case SADB_EXT_SUPPORTED_ENCRYPT:
		case SADB_EXT_SPIRANGE:
		case SADB_X_EXT_KMPRIVATE:
		case SADB_X_EXT_SATYPE2:
		case SADB_X_EXT_SA2:
		case SADB_X_EXT_ADDRESS_DST2:
		case SADB_X_EXT_DEBUG:
		default:
		{
			unsigned int elen;
			unsigned char *bytes;

dumpbytes:

			elen = IPSEC_PFKEYv2_LEN(se->sadb_ext_len) -
			       sizeof(struct sadb_ext);
			bytes = (unsigned char *)&se[1];

			fprintf(out, "bytes=0x");
			while (elen > 0) {
				fprintf(out, "%02x", *bytes);
				bytes++;
				elen--;
			}
			fprintf(out, " } ");
		}
		break;
		}

		/* skip to next extension header */
		{
			unsigned int elen =
				IPSEC_PFKEYv2_LEN(se->sadb_ext_len);

			if (elen < sizeof(struct sadb_ext)) {
				fprintf(out, "illegal-length(%d) ", elen);
				elen = sizeof(struct sadb_ext);
			}

			se = (struct sadb_ext *)(((unsigned char *)se) + elen);
			len -= elen;
		}
	}
	fprintf(out, "\n");
}

