[BACK]Return to print-ike.c CVS log [TXT][DIR] Up to [local] / src / usr.sbin / tcpdump

File: [local] / src / usr.sbin / tcpdump / print-ike.c (download)

Revision 1.7, Wed Apr 18 09:14:14 2001 UTC (23 years, 1 month ago) by niklas
Branch: MAIN
CVS Tags: OPENBSD_3_0_BASE, OPENBSD_3_0, OPENBSD_2_9_BASE, OPENBSD_2_9
Changes since 1.6: +18 -22 lines

Correct IKE mode config printouts, ok ho@

/*	$OpenBSD: print-ike.c,v 1.7 2001/04/18 09:14:14 niklas Exp $	*/

/*
 * Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999
 *	The Regents of the University of California.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that: (1) source code distributions
 * retain the above copyright notice and this paragraph in its entirety, (2)
 * distributions including binary code include the above copyright notice and
 * this paragraph in its entirety in the documentation or other materials
 * provided with the distribution, and (3) all advertising materials mentioning
 * features or use of this software display the following acknowledgement:
 * ``This product includes software developed by the University of California,
 * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
 * the University nor the names of its contributors may be used to endorse
 * or promote products derived from this software without specific prior
 * written permission.
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 *
 * Format and print ike (isakmp) packets.
 *	By Tero Kivinen <kivinen@ssh.fi>, Tero Mononen <tmo@ssh.fi>,
 *         Tatu Ylonen <ylo@ssh.fi> and Timo J. Rinne <tri@ssh.fi>
 *         in co-operation with SSH Communications Security, Espoo, Finland
 *
 * Rewritten and extended (quite a lot, too) by Hakan Olsson <ho@openbsd.org>
 *
 */

#ifndef lint
static const char rcsid[] =
    "@(#) $Header: /cvsrepo/anoncvs/cvs/src/usr.sbin/tcpdump/print-ike.c,v 1.7 2001/04/18 09:14:14 niklas Exp $ (XXX)";
#endif

#include <sys/param.h>
#include <sys/time.h>
#include <sys/socket.h>

#ifdef __STDC__
struct mbuf;
struct rtentry;
#endif

#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <ctype.h>
#include <stdio.h>
#include <string.h>

#include "interface.h"
#include "addrtoname.h"
#include "ike.h"

struct isakmp_header {
	u_char  init_cookie[8];
	u_char  resp_cookie[8];
	u_char  nextpayload;
	u_char  version;
	u_char  exgtype;
	u_char  flags;
	u_char  msgid[4];
	u_int32_t length;
	u_char  payloads[0];  
};

struct notification_payload {
	u_char    next_payload;
	u_char    reserved;
	u_int16_t payload_length;
	u_int32_t doi;
	u_char    protocol_id;
  	u_char    spi_size;
  	u_int16_t type;
	u_char    data[0];
};

static void ike_pl_print(register u_char, register u_char *, u_char);

int ike_tab_level = 0;
u_char xform_proto;

static const char *ike[] = IKE_PROTO_INITIALIZER;

#define SMALL_TABS 4
#define SPACES "                                                   "
const char *
ike_tab_offset (void)
{
  const char *p, *endline;
  static const char line[] = SPACES;

  endline = line + sizeof line - 1;
  p = endline - SMALL_TABS * (ike_tab_level);  

  return (p > line ? p : line);
}

static char *
ike_get_cookie (u_char *ic, u_char *rc)
{
	static char cookie_jar[35];
	register int i;
	cookie_jar[0] = cookie_jar[34] = 0;

	for (i = 0; i < 8; i++)
		sprintf (cookie_jar + (i<<1), "%02x", *(ic + i));
	strcat (cookie_jar, "->");
	for (i = 0; i < 8; i++)
		sprintf (cookie_jar + 18 + (i<<1), "%02x", *(rc + i));

	return cookie_jar;
}

/*
 * Print isakmp requests
 */
void
ike_print (register const u_char *cp, register u_int length)
{
	struct isakmp_header *ih;
	register const u_char *ep;
	u_char *payload;
	u_char  nextpayload;
	int encrypted;
	static const char *exgtypes[] = IKE_EXCHANGE_TYPES_INITIALIZER;

	encrypted = 0;

#ifdef TCHECK
#undef TCHECK
#endif
#define TCHECK(var, l) if ((u_char *)&(var) > ep - l) goto trunc
	
	ih = (struct isakmp_header *)cp;
	/* Note funny sized packets */
	if (length < 20) {
		(void)printf(" [len=%d]", length);
	}

	/* 'ep' points to the end of avaible data. */
	ep = snapend;

	printf(" isakmp");

	printf(" v%d.%d", ih->version >> 4, ih->version & 0xf);

	printf(" exchange ");
	if (ih->exgtype < (sizeof exgtypes/sizeof exgtypes[0]))
	        printf("%s", exgtypes[ih->exgtype]);
	else
	        printf("%d (unknown)", ih->exgtype);

	if (ih->flags & FLAGS_ENCRYPTION) {
		printf(" encrypted");
		encrypted = 1;
	}
	
	if (ih->flags & FLAGS_COMMIT) {
		printf(" commit");
	}

	printf("\n\tcookie: %s", ike_get_cookie (ih->init_cookie,
						 ih->resp_cookie));

	TCHECK(ih->msgid, sizeof(ih->msgid));
	printf(" msgid: %02x%02x%02x%02x",
	       ih->msgid[0], ih->msgid[1],
	       ih->msgid[2], ih->msgid[3]);

	TCHECK(ih->length, sizeof(ih->length));
	printf(" len: %d", ntohl(ih->length));
	
	if (ih->version > 16) {
		printf(" new version");
		return;
	}

	payload = ih->payloads;
	nextpayload = ih->nextpayload;

	/* if encrypted, then open special file for encryption keys */
	if (encrypted) {
		/* decrypt XXX */
		return;
	}

	/* if verbose, print payload data */
	if (vflag)
	        ike_pl_print(nextpayload, payload, ISAKMP_DOI);

	return;

trunc:
	fputs(" [|isakmp]", stdout);
}

void
ike_pl_sa_print (register u_char *buf, register int len)
{
	u_int32_t situation = ntohl(*(u_int32_t *)(buf + 4));
	u_char ike_doi = ntohl((*(u_int32_t *)buf));
	printf(" DOI: %d", ike_doi);
	if (ike_doi == IPSEC_DOI) {
	        printf("(IPSEC) situation: ");
		if (situation & IKE_SITUATION_IDENTITY_ONLY)
		        printf("IDENTITY_ONLY ");
		if (situation & IKE_SITUATION_SECRECY)
		        printf("SECRECY ");
		if (situation & IKE_SITUATION_INTEGRITY)
		        printf("INTEGRITY ");
		if ((situation & IKE_SITUATION_MASK) == 0)
		        printf("0x%x (unknown)", situation);
	        ike_pl_print (PAYLOAD_PROPOSAL, buf + 8, IPSEC_DOI);
	}
	else
	        printf(" situation: (unknown)");
}

int
ike_attribute_print (register u_char *buf, u_char doi, register int maxlen)
{
	static char *attrs[] = IKE_ATTR_INITIALIZER;
	static char *attr_enc[] = IKE_ATTR_ENCRYPT_INITIALIZER;
	static char *attr_hash[] = IKE_ATTR_HASH_INITIALIZER;
	static char *attr_auth[] = IKE_ATTR_AUTH_INITIALIZER;
	static char *attr_gdesc[] = IKE_ATTR_GROUP_DESC_INITIALIZER;
	static char *attr_gtype[] = IKE_ATTR_GROUP_INITIALIZER;
	static char *attr_ltype[] = IKE_ATTR_SA_DURATION_INITIALIZER;
	static char *ipsec_attrs[] = IPSEC_ATTR_INITIALIZER;
	static char *ipsec_attr_encap[] = IPSEC_ATTR_ENCAP_INITIALIZER;
	static char *ipsec_attr_auth[] = IPSEC_ATTR_AUTH_INITIALIZER;
	static char *ipsec_attr_ltype[] = IPSEC_ATTR_DURATION_INITIALIZER;

	u_char    af   = buf[0] >> 7;
	u_int16_t type = (buf[0] & 0x7f) << 8 | buf[1];
	u_int16_t len  = buf[2] << 8 | buf[3], val;

	if (doi == ISAKMP_DOI) 
		printf("\n\t%sattribute %s = ", ike_tab_offset(),
		       (type < sizeof attrs / sizeof attrs[0] ?
			attrs[type] : "<unknown>"));
	else
		printf("\n\t%sattribute %s = ", ike_tab_offset(),
		       (type < (sizeof ipsec_attrs / sizeof ipsec_attrs[0]) ?
			ipsec_attrs[type] : "<unknown>"));

	if ((af == 1 && maxlen < 4) ||
	    (af == 0 && maxlen < (len + 4))) {
		printf ("\n\t%s[|attr]", ike_tab_offset());
		return maxlen;
	}

	if (af == 0) {
		/* AF=0; print the variable length attribute value */
		for (val = 0; val < len; val++)
			printf("%02x", (char)*(buf + 4 + val));
		return len + 4;
	}

	val = len;	/* For AF=1, this field is the "VALUE" */
	len = 4; 	/* and with AF=1, length is always 4 */

#define CASE_PRINT(TYPE,var) \
        case TYPE : \
               	if (val < sizeof var / sizeof var [0]) \
                       	printf("%s", var [val]); \
               	else \
                       	printf("%d (unknown)", val); \
               	break;

	if (doi == ISAKMP_DOI)
		switch(type) {
			CASE_PRINT(IKE_ATTR_ENCRYPTION_ALGORITHM, attr_enc);
			CASE_PRINT(IKE_ATTR_HASH_ALGORITHM, attr_hash);
			CASE_PRINT(IKE_ATTR_AUTHENTICATION_METHOD, attr_auth);
			CASE_PRINT(IKE_ATTR_GROUP_DESC, attr_gdesc);
			CASE_PRINT(IKE_ATTR_GROUP_TYPE, attr_gtype);
			CASE_PRINT(IKE_ATTR_LIFE_TYPE, attr_ltype);
		default:
			printf("%d", val);
		}
	else
		switch(type) {
			CASE_PRINT(IPSEC_ATTR_SA_LIFE_TYPE, ipsec_attr_ltype);
			CASE_PRINT(IPSEC_ATTR_ENCAPSULATION_MODE,
				   ipsec_attr_encap);
			CASE_PRINT(IPSEC_ATTR_AUTHENTICATION_ALGORITHM,
				   ipsec_attr_auth);
		default:
			printf ("%d", val);
		}

#undef CASE_PRINT

	return len;
}

void
ike_pl_transform_print (register u_char *buf, register int len, u_char doi)
{
	const char *ah[] = IPSEC_AH_INITIALIZER;
	const char *esp[] = IPSEC_ESP_INITIALIZER;
	u_char *attr = buf + 4;

	printf("\n\t%stransform: %u ID: ", ike_tab_offset(), buf[0]);
	
	switch (doi) {
	case ISAKMP_DOI:
		if (buf[1] < (sizeof ike / sizeof ike[0]))
			printf ("%s", ike[buf[1]]);
		else
			printf ("%d(unknown)", buf[1]);
		break;

	default: /* IPSEC_DOI */
		switch (xform_proto) {	/* from ike_proposal_print */
		case PROTO_IPSEC_AH:
			if (buf[1] < (sizeof ah / sizeof ah[0]))
				printf ("%s", ah[buf[1]]);
			else
				printf ("%d(unknown)", buf[1]);
			break;
		case PROTO_IPSEC_ESP:
			if (buf[1] < (sizeof esp / sizeof esp[0]))
				printf ("%s", esp[buf[1]]);
			else
				printf ("%d(unknown)", buf[1]);
			break;
		default:
			printf ("%d(unknown)", buf[1]);
		}
		break;
	}
 
	ike_tab_level++;
	while((int)(attr - buf) < len - 4)  /* Skip last 'NONE' attr */
		attr += ike_attribute_print(attr, doi, len - (attr-buf));
	ike_tab_level--;
}

void
ike_pl_proposal_print (register u_char *buf, register int len, u_char doi)
{
	printf(" proposal: %d proto: %s spisz: %d xforms: %d", 
	       buf[0], (buf[1] < (sizeof ike / sizeof ike[0]) ? ike[buf[1]] : 
			"(unknown)"), buf[2], buf[3]);

	/* We need to store this for upcoming ike_attribute_print call. */
	xform_proto = buf[1];

	if (buf[2]) {
		/* XXX it is possible that spisz may be != 4 ... */
		printf(" SPI: 0x%08x", 
		       buf[4]<<24 | buf[5]<<16 | buf[6]<<8 | buf[7]);
		doi = IPSEC_DOI;
	} else 
		doi = ISAKMP_DOI;

	if((char)buf[3] > 0)
		ike_pl_print(PAYLOAD_TRANSFORM, buf+4+buf[2], doi);
}

void
ike_pl_ke_print (register u_char *buf, register int len, u_char doi)
{
	if (doi != IPSEC_DOI)
		return;

	/* XXX ... */
}
	
void
ipsec_id_print (register u_char *buf, register int len, u_char doi)
{
	static const char *idtypes[] = IPSEC_ID_TYPE_INITIALIZER;
	char ntop_buf[INET6_ADDRSTRLEN];
	struct in_addr in;
	u_char *p;

	if (doi != ISAKMP_DOI) 
		return;

	/* Don't print proto+port unless actually used */
	if (buf[1] | buf[2] | buf[3])
		printf (" proto: %d port: %d", buf[1], (buf[2] << 8) + buf[3]);

	printf (" type: %s = ", buf[0] < (sizeof idtypes/sizeof idtypes[0]) ?
		idtypes[buf[0]] : "<unknown>");

	switch (buf[0]) {
	case IPSEC_ID_IPV4_ADDR:
		memcpy (&in.s_addr, buf + 4, sizeof in);
		printf ("%s", inet_ntoa (in));
		break;
	case IPSEC_ID_IPV4_ADDR_SUBNET:
	case IPSEC_ID_IPV4_ADDR_RANGE:
		memcpy (&in.s_addr, buf + 4, sizeof in);
		printf ("%s%s", inet_ntoa (in),
			buf[0] == IPSEC_ID_IPV4_ADDR_SUBNET ? "/" : "-");
		memcpy (&in.s_addr, buf + 8, sizeof in);
		printf ("%s", inet_ntoa (in));
		break;

	case IPSEC_ID_IPV6_ADDR:
		printf ("%s", inet_ntop (AF_INET6, buf + 4, ntop_buf,
					 sizeof ntop_buf));
		break;
	case IPSEC_ID_IPV6_ADDR_SUBNET:
	case IPSEC_ID_IPV6_ADDR_RANGE:
		printf ("%s%s", inet_ntop (AF_INET6, buf + 4, ntop_buf,
					   sizeof ntop_buf),
			buf[0] == IPSEC_ID_IPV6_ADDR_SUBNET ? "/" : "-");
		printf ("%s", inet_ntop (AF_INET6, buf + 4 + sizeof ntop_buf,
					 ntop_buf, sizeof ntop_buf));

	case IPSEC_ID_FQDN:
	case IPSEC_ID_USER_FQDN:
		printf ("\"");
		for(p = buf + 4; (int)(p - buf) < len; p++)
			printf ("%c",(isprint(*p) ? *p : '.'));
		printf ("\"");
		break;

	case IPSEC_ID_DER_ASN1_DN:
	case IPSEC_ID_DER_ASN1_GN:
	case IPSEC_ID_KEY_ID:
	default:
		printf ("\"(not shown)\"");
		break;
	}
}

void
ike_pl_notification_print (register u_char *buf, register int len)
{
  	static const char *nftypes[] = IKE_NOTIFY_TYPES_INITIALIZER;
  	struct notification_payload *np = (struct notification_payload *)buf;
	u_int32_t *replay;
	u_char *attr;

	if (len < sizeof (struct notification_payload)) {
		printf (" (|len)");
		return;
	}

	np->doi  = ntohl (np->doi);
	np->type = ntohs (np->type);

	if (np->doi != ISAKMP_DOI && np->doi != IPSEC_DOI) {
		printf (" (unknown DOI)");
		return;
	}

	printf ("\n\t%snotification: ", ike_tab_offset());

	if (np->type > 0 && np->type < (sizeof nftypes / sizeof nftypes[0]))
		printf("%s", nftypes[np->type]);
	else switch (np->type) {

	case NOTIFY_IPSEC_RESPONDER_LIFETIME:
		printf("RESPONDER LIFETIME");
		if (np->spi_size == 16)
			printf ("(%s)", ike_get_cookie (&np->data[0], 
							&np->data[8]));
		else
			printf("SPI: 0x%08x", np->data[0]<<24 | 
			       np->data[1]<<16 | np->data[2]<<8 | np->data[3]);
		attr = &np->data[np->spi_size];
		ike_tab_level++;
		while((int)(attr - buf) < len - 4)  /* Skip last 'NONE' attr */
			attr += ike_attribute_print(attr, IPSEC_DOI,
						    len - (attr-buf));
		ike_tab_level--;	
		break;

	case NOTIFY_IPSEC_REPLAY_STATUS:
		replay = (u_int32_t *)&np->data[np->spi_size];
		printf("REPLAY STATUS [%sabled] ", *replay ? "en" : "dis");
		if (np->spi_size == 16)
			printf ("(%s)", ike_get_cookie (&np->data[0], 
							&np->data[8]));
		else
			printf("SPI: 0x%08x", np->data[0]<<24 | 
			       np->data[1]<<16 | np->data[2]<<8 | np->data[3]);
		break; 

	case NOTIFY_IPSEC_INITIAL_CONTACT:
		printf("INITIAL CONTACT (%s)", ike_get_cookie (&np->data[0],
							       &np->data[8]));
		break;

	default:
	  	printf("%d (unknown)", np->type);
		break;
	}
  	return;
}
	
void
ike_pl_vendor_print (register u_char *buf, register int len, u_char doi)
{
	u_char *p = buf;

	if (doi != IPSEC_DOI) 
		return;

	printf(" \"");
	for(p = buf; (int)(p - buf) < len; p++)
	        printf("%c",(isprint(*p) ? *p : '.'));
	printf("\"");
}

/* IKE mode-config. */
int
ike_cfg_attribute_print (register u_char *buf, register int attr_type, 
			 register int maxlen)
{
	static char *attrs[] = IKE_CFG_ATTRIBUTE_INITIALIZER;
	char ntop_buf[INET6_ADDRSTRLEN];
	struct in_addr in;

	u_char    af   = buf[0] >> 7;
	u_int16_t type = (buf[0] & 0x7f) << 8 | buf[1];
	u_int16_t len  = af ? 2 : buf[2] << 8 | buf[3], p;
	u_char   *val  = af ? buf + 2 : buf + 4;

	printf("\n\t\%sattribute %s = ", ike_tab_offset(),
	       type < (sizeof attrs / sizeof attrs[0]) ? attrs[type] : 
	       "<unknown>");

	if ((af == 1 && maxlen < 4) ||
	    (af == 0 && maxlen < (len + 4))) {
		printf ("\n\t%s[|attr]", ike_tab_offset());
		return maxlen;
	}

	/* XXX The 2nd term is for bug compatibility with PGPnet.  */
	if (len == 0 || (af && !val[0] && !val[1])) {
		printf ("<none>");
		return 4;
	}
	
	/* XXX Generally lengths are not checked well below.  */
	switch (type) {
	case IKE_CFG_ATTR_INTERNAL_IP4_ADDRESS:
	case IKE_CFG_ATTR_INTERNAL_IP4_NETMASK:
	case IKE_CFG_ATTR_INTERNAL_IP4_DNS:
	case IKE_CFG_ATTR_INTERNAL_IP4_NBNS:
	case IKE_CFG_ATTR_INTERNAL_IP4_DHCP:
		memcpy (&in.s_addr, val, sizeof in);
		printf ("%s", inet_ntoa (in));
		break;
		
	case IKE_CFG_ATTR_INTERNAL_IP6_ADDRESS:
	case IKE_CFG_ATTR_INTERNAL_IP6_NETMASK:
	case IKE_CFG_ATTR_INTERNAL_IP6_DNS:
	case IKE_CFG_ATTR_INTERNAL_IP6_NBNS:
	case IKE_CFG_ATTR_INTERNAL_IP6_DHCP:
		printf ("%s", inet_ntop (AF_INET6, val, ntop_buf,
					 sizeof ntop_buf));
		break;

	case IKE_CFG_ATTR_INTERNAL_IP4_SUBNET:
		memcpy(&in.s_addr, val, sizeof in);
		printf("%s/", inet_ntoa (in));
		memcpy(&in.s_addr, val + sizeof in, sizeof in);
		printf("%s", inet_ntoa (in));
		break;
		
	case IKE_CFG_ATTR_INTERNAL_IP6_SUBNET:
		printf("%s/%u", inet_ntop (AF_INET6, val, ntop_buf, 
					   sizeof ntop_buf),
		       val[16]);
		break;
		
	case IKE_CFG_ATTR_INTERNAL_ADDRESS_EXPIRY:
		printf("%u seconds",
		       val[0] << 24 | val[1] << 16 | val[2] << 8 | val[3]);
		break;

	case IKE_CFG_ATTR_APPLICATION_VERSION:
		for (p = 0; p < len; p++)
			printf("%c", isprint(val[p]) ? val[p] : '.');
		break;
		
	case IKE_CFG_ATTR_SUPPORTED_ATTRIBUTES:
		printf("<%d attributes>", len / 2);
		ike_tab_level++;
		for (p = 0; p < len; p += 2) {
			type = (val[p] << 8 | val[p + 1]) & 0x7fff;
			printf("\n\t%s%s", ike_tab_offset(),
			       type < (sizeof attrs/sizeof attrs[0]) ?
			       attrs[type] : "<unknown>");
		}
		ike_tab_level--;
		break;
		
	default:
		break;
	}
	return af ? 4 : len + 4;
}

void
ike_pl_attribute_print (register u_char *buf, register int len)
{
	static const char *pl_attr[] = IKE_CFG_ATTRIBUTE_TYPE_INITIALIZER;
	u_char type, *attr;
	u_int16_t id;

	type = buf[0];
	id = buf[2]<<8 | buf[3];
	attr = buf + 4;

	printf(" type: %s Id: %d", 
	       type < (sizeof pl_attr/sizeof pl_attr[0]) ? pl_attr[type] :
	       "<unknown>", id);

	while((int)(attr - buf) < len - 4)  
		attr += ike_cfg_attribute_print(attr, type, len - (attr-buf));
}

void
ike_pl_print (register u_char type, register u_char *buf, u_char doi)
{
	static const char *pltypes[] = IKE_PAYLOAD_TYPES_INITIALIZER;
	int next_type = buf[0];
	int this_len = buf[2]<<8 | buf[3];

	printf("\n\t%spayload: %s len: %d", ike_tab_offset(),
	       (type < (sizeof pltypes/sizeof pltypes[0]) ?
		pltypes[type] : "<unknown>"), this_len);

	if ((u_char *)&(buf[0]) > snapend - this_len) 
	  goto pltrunc;

	ike_tab_level++;
	switch(type) {
	case PAYLOAD_NONE:
		return;

	case PAYLOAD_SA:
		ike_pl_sa_print(buf+4, this_len);
		break;
	    
	case PAYLOAD_PROPOSAL:
		ike_pl_proposal_print(buf+4, this_len, doi);
		break;
	    
	case PAYLOAD_TRANSFORM:
		ike_pl_transform_print(buf+4, this_len, doi);
		break;
	    
	case PAYLOAD_KE:
		ike_pl_ke_print(buf+4, this_len, doi);
		break;
	    
	case PAYLOAD_ID:
		/* Should only happen with IPsec DOI */
	        ipsec_id_print(buf+4, this_len, doi);
		break;

	case PAYLOAD_CERT:
	case PAYLOAD_CERTREQUEST:
	case PAYLOAD_HASH:
	case PAYLOAD_SIG:
	case PAYLOAD_NONCE:
	case PAYLOAD_DELETE:
		break;
	    
	case PAYLOAD_NOTIFICATION:
	  	ike_pl_notification_print(buf, this_len);
		break;

	case PAYLOAD_VENDOR:
	        ike_pl_vendor_print(buf+4, this_len, doi);
		break;

	case PAYLOAD_ATTRIBUTE:
	        ike_pl_attribute_print(buf+4, this_len);
		break;

	default:
	}
	ike_tab_level--;

	if(next_type)  /* Recurse over next payload */
	        ike_pl_print(next_type, buf + this_len, doi);

	return;

pltrunc:
	if (doi == ISAKMP_DOI)
		fputs(" [|isakmp]", stdout);
	else
		fputs(" [|ipsec]", stdout);

}