/*	$OpenBSD: print-l2tp.c,v 1.1 2000/01/16 10:54:58 jakob Exp $	*/

/*
 * Copyright (c) 1991, 1993, 1994, 1995, 1996, 1997
 *      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.
 *
 * L2TP support contributed by Motonori Shindo (mshindo@ascend.co.jp)
 */

#ifndef lint
static const char rcsid[] =
    "@(#) $Header: /cvs/OpenBSD/src/usr.sbin/tcpdump/print-l2tp.c,v 1.1 2000/01/16 10:54:58 jakob Exp $";
#endif

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

#include "l2tp.h"
#include "interface.h"

static char tstr[] = " [|l2tp]";

#ifndef TRUE
#define TRUE 1
#endif

#ifndef FALSE
#define FALSE 0
#endif

static u_char *l2tp_message_type_string[] = {
	"RESERVED_0",		/* 0  Reserved */
	"SCCRQ",		/* 1  Start-Control-Connection-Request */
	"SCCRP",		/* 2  Start-Control-Connection-Reply */
	"SCCCN",		/* 3  Start-Control-Connection-Connected */
	"StopCCN",		/* 4  Stop-Control-Connection-Notification */
	"RESERVED_5",		/* 5  Reserved */
	"HELLO",		/* 6  Hello */
	"OCRQ",			/* 7  Outgoing-Call-Request */
	"OCRP",			/* 8  Outgoing-Call-Reply */
	"OCCN",			/* 9  Outgoing-Call-Connected */
	"ICRQ",			/* 10 Incoming-Call-Request */
	"ICRP",			/* 11 Incoming-Call-Reply */
	"ICCN",			/* 12 Incoming-Call-Connected */
	"RESERVED_13",		/* 13 Reserved */
	"CDN",			/* 14 Call-Disconnect-Notify */
	"WEN",			/* 15 WAN-Error-Notify */
	"SLI"			/* 16 Set-Link-Info */
#define L2TP_MAX_MSGTYPE_INDEX	17
};

static void l2tp_msgtype_print(const u_char *dat, u_int length);
static void l2tp_result_code_print(const u_char *dat, u_int length);
static void l2tp_proto_ver_print(const u_char *dat, u_int length);
static void l2tp_framing_cap_print(const u_char *dat, u_int length);
static void l2tp_bearer_cap_print(const u_char *dat, u_int length);
static void l2tp_tie_breaker_print(const u_char *dat, u_int length);
static void l2tp_firm_ver_print(const u_char *dat, u_int length);
static void l2tp_host_name_print(const u_char *dat, u_int length);
static void l2tp_vendor_name_print(const u_char *dat, u_int length);
static void l2tp_assnd_tun_id_print(const u_char *dat, u_int length);
static void l2tp_recv_win_size_print(const u_char *dat, u_int length);
static void l2tp_challenge_print(const u_char *dat, u_int length);
static void l2tp_q931_cc_print(const u_char *dat, u_int length);
static void l2tp_challenge_resp_print(const u_char *dat, u_int length);
static void l2tp_assnd_sess_id_print(const u_char *dat, u_int length);
static void l2tp_call_ser_num_print(const u_char *dat, u_int length);
static void l2tp_minimum_bps_print(const u_char *dat, u_int length);
static void l2tp_maximum_bps_print(const u_char *dat, u_int length);
static void l2tp_bearer_type_print(const u_char *dat, u_int length);
static void l2tp_framing_type_print(const u_char *dat, u_int length);
static void l2tp_packet_proc_delay_print(const u_char *dat, u_int length);
static void l2tp_called_number_print(const u_char *dat, u_int length);
static void l2tp_calling_number_print(const u_char *dat, u_int length);
static void l2tp_sub_address_print(const u_char *dat, u_int length);
static void l2tp_tx_conn_speed_print(const u_char *dat, u_int length);
static void l2tp_phy_channel_id_print(const u_char *dat, u_int length);
static void l2tp_ini_recv_lcp_print(const u_char *dat, u_int length);
static void l2tp_last_sent_lcp_print(const u_char *dat, u_int length);
static void l2tp_last_recv_lcp_print(const u_char *dat, u_int length);
static void l2tp_proxy_auth_type_print(const u_char *dat, u_int length);
static void l2tp_proxy_auth_name_print(const u_char *dat, u_int length);
static void l2tp_proxy_auth_chal_print(const u_char *dat, u_int length);
static void l2tp_proxy_auth_id_print(const u_char *dat, u_int length);
static void l2tp_proxy_auth_resp_print(const u_char *dat, u_int length);
static void l2tp_call_errors_print(const u_char *dat, u_int length);
static void l2tp_accm_print(const u_char *dat, u_int length);
static void l2tp_random_vector_print(const u_char *dat, u_int length);
static void l2tp_private_grp_id_print(const u_char *dat, u_int length);
static void l2tp_rx_conn_speed_print(const u_char *dat, u_int length);
static void l2tp_seq_required_print(const u_char *dat, u_int length);
static void l2tp_avp_print(const u_char *dat, u_int length);

static struct l2tp_avp_vec l2tp_avp[] = {
  {"MSGTYPE", l2tp_msgtype_print}, 		/* 0  Message Type */
  {"RESULT_CODE", l2tp_result_code_print},	/* 1  Result Code */
  {"PROTO_VER", l2tp_proto_ver_print},		/* 2  Protocol Version */
  {"FRAMING_CAP", l2tp_framing_cap_print},	/* 3  Framing Capabilities */
  {"BEARER_CAP", l2tp_bearer_cap_print},	/* 4  Bearer Capabilities */
  {"TIE_BREAKER", l2tp_tie_breaker_print},	/* 5  Tie Breaker */
  {"FIRM_VER", l2tp_firm_ver_print},		/* 6  Firmware Revision */
  {"HOST_NAME", l2tp_host_name_print},		/* 7  Host Name */
  {"VENDOR_NAME", l2tp_vendor_name_print},	/* 8  Vendor Name */
  {"ASSND_TUN_ID", l2tp_assnd_tun_id_print}, 	/* 9  Assigned Tunnel ID */
  {"RECV_WIN_SIZE", l2tp_recv_win_size_print},	/* 10 Receive Window Size */
  {"CHALLENGE", l2tp_challenge_print},		/* 11 Challenge */
  {"Q931_CC", l2tp_q931_cc_print},		/* 12 Q.931 Cause Code */
  {"CHALLENGE_RESP", l2tp_challenge_resp_print},/* 13 Challenge Response */
  {"ASSND_SESS_ID", l2tp_assnd_sess_id_print},  /* 14 Assigned Session ID */
  {"CALL_SER_NUM", l2tp_call_ser_num_print}, 	/* 15 Call Serial Number */
  {"MINIMUM_BPS",	l2tp_minimum_bps_print},/* 16 Minimum BPS */
  {"MAXIMUM_BPS", l2tp_maximum_bps_print},	/* 17 Maximum BPS */
  {"BEARER_TYPE",	l2tp_bearer_type_print},/* 18 Bearer Type */
  {"FRAMING_TYPE", l2tp_framing_type_print}, 	/* 19 Framing Type */
  {"PACKET_PROC_DELAY", l2tp_packet_proc_delay_print}, /* 20 Packet Processing Delay (OBSOLETE) */
  {"CALLED_NUMBER", l2tp_called_number_print},	/* 21 Called Number */
  {"CALLING_NUMBER", l2tp_calling_number_print},/* 22 Calling Number */
  {"SUB_ADDRESS",	l2tp_sub_address_print},/* 23 Sub-Address */
  {"TX_CONN_SPEED", l2tp_tx_conn_speed_print},	/* 24 (Tx) Connect Speed */
  {"PHY_CHANNEL_ID", l2tp_phy_channel_id_print},/* 25 Physical Channel ID */
  {"INI_RECV_LCP", l2tp_ini_recv_lcp_print}, 	/* 26 Initial Received LCP CONFREQ */
  {"LAST_SENT_LCP", l2tp_last_sent_lcp_print},	/* 27 Last Sent LCP CONFREQ */
  {"LAST_RECV_LCP", l2tp_last_recv_lcp_print},	/* 28 Last Received LCP CONFREQ */
  {"PROXY_AUTH_TYPE", l2tp_proxy_auth_type_print},/* 29 Proxy Authen Type */
  {"PROXY_AUTH_NAME", l2tp_proxy_auth_name_print},/* 30 Proxy Authen Name */
  {"PROXY_AUTH_CHAL", l2tp_proxy_auth_chal_print},/* 31 Proxy Authen Challenge */
  {"PROXY_AUTH_ID", l2tp_proxy_auth_id_print},	/* 32 Proxy Authen ID */
  {"PROXY_AUTH_RESP", l2tp_proxy_auth_resp_print},/* 33 Proxy Authen Response */
  {"CALL_ERRORS", l2tp_call_errors_print},	/* 34 Call Errors */
  {"ACCM", l2tp_accm_print},			/* 35 ACCM */
  {"RANDOM_VECTOR", l2tp_random_vector_print},	/* 36 Random Vector */
  {"PRIVATE_GRP_ID", l2tp_private_grp_id_print},/* 37 Private Group ID */
  {"RX_CONN_SPEED", l2tp_rx_conn_speed_print},	/* 38 (Rx) Connect Speed */
  {"SEQ_REQUIRED", l2tp_seq_required_print}, 	/* 39 Sequencing Required */
#define L2TP_MAX_AVP_INDEX	40
};

#if 0
static char *l2tp_result_code_StopCCN[] = {
         "Reserved",
         "General request to clear control connection",
         "General error--Error Code indicates the problem",
         "Control channel already exists",
         "Requester is not authorized to establish a control channel",
         "The protocol version of the requester is not supported",
         "Requester is being shut down",
         "Finite State Machine error"
#define L2TP_MAX_RESULT_CODE_STOPCC_INDEX	8
};
#endif

#if 0
static char *l2tp_result_code_CDN[] = {
	"Reserved",
	"Call disconnected due to loss of carrier",
	"Call disconnected for the reason indicated in error code",
	"Call disconnected for administrative reasons",
	"Call failed due to lack of appropriate facilities being " \
	"available (temporary condition)",
	"Call failed due to lack of appropriate facilities being " \
	"available (permanent condition)",
	"Invalid destination",
	"Call failed due to no carrier detected",
	"Call failed due to detection of a busy signal",
	"Call failed due to lack of a dial tone",
	"Call was not established within time allotted by LAC",
	"Call was connected but no appropriate framing was detected"
#define L2TP_MAX_RESULT_CODE_CDN_INDEX	12
};
#endif

#if 0
static char *l2tp_error_code_general[] = {
	"No general error",
	"No control connection exists yet for this LAC-LNS pair",
	"Length is wrong",
	"One of the field values was out of range or " \
	"reserved field was non-zero"
	"Insufficient resources to handle this operation now",
	"The Session ID is invalid in this context",
	"A generic vendor-specific error occurred in the LAC",
	"Try another"
#define L2TP_MAX_ERROR_CODE_GENERAL_INDEX	8
};
#endif

/******************************/
/* generic print out routines */
/******************************/
static void 
print_string(const u_char *dat, u_int length)
{
	int i;
	for (i=0; i<length; i++) {
		printf("%c", *dat++);
	}
}

static void 
print_octets(const u_char *dat, u_int length)
{
	int i;
	for (i=0; i<length; i++) {
		printf("%02x", *dat++);
	}
}

static void
print_short(const u_short *dat)
{
	printf("%u", ntohs(*dat));
}

static void
print_int(const u_int *dat)
{
	printf("%lu", (u_long)ntohl(*dat));
}

/**********************************/
/* AVP-specific print out routines*/
/**********************************/
static void
l2tp_msgtype_print(const u_char *dat, u_int length)
{
	u_short *ptr = (u_short *)dat;

	if (ntohs(*ptr) < L2TP_MAX_MSGTYPE_INDEX) {
		printf("%s", l2tp_message_type_string[ntohs(*ptr)]);
	}
}

static void
l2tp_result_code_print(const u_char *dat, u_int length)
{
	/* we just print out the result and error code number */
	u_short *ptr = (u_short *)dat;
	
	if (length == 2) {		/* result code */
		printf("%d", ntohs(*ptr));	
	} else if (length == 4) { 	/* result & error code */
		printf("%d/%d", ntohs(*ptr), ntohs(*(ptr+1)));
	} else if (length > 4) {	/* result & error code & msg */
		printf("%u/%u ", ntohs(*ptr), ntohs(*(ptr+1)));
		print_string((u_char *)(ptr+2), length - 4);
	}
}

static void
l2tp_proto_ver_print(const u_char *dat, u_int length)
{
	printf("%d.%d", *dat, *(dat+1));
}


static void
l2tp_framing_cap_print(const u_char *dat, u_int length)
{
	u_int *ptr = (u_int *)dat;

	if (ntohl(*ptr) &  L2TP_FRAMING_CAP_ASYNC_MASK) {
		printf("A");
	}
	if (ntohl(*ptr) &  L2TP_FRAMING_CAP_SYNC_MASK) {
		printf("S");
	}
}

static void
l2tp_bearer_cap_print(const u_char *dat, u_int length)
{
	u_int *ptr = (u_int *)dat;

	if (ntohl(*ptr) &  L2TP_BEARER_CAP_ANALOG_MASK) {
		printf("A");
	}
	if (ntohl(*ptr) &  L2TP_BEARER_CAP_DIGITAL_MASK) {
		printf("D");
	}
}

static void
l2tp_tie_breaker_print(const u_char *dat, u_int length)
{
	printf("%lx", *(u_long *)dat);	/* XXX */
}

static void
l2tp_firm_ver_print(const u_char *dat, u_int length)
{
	print_short((u_short *)dat);
}

static void
l2tp_host_name_print(const u_char *dat, u_int length)
{
	print_string(dat, length);
}

static void
l2tp_vendor_name_print(const u_char *dat, u_int length)
{
	print_string(dat, length);
}

static void
l2tp_assnd_tun_id_print(const u_char *dat, u_int length)
{
	print_short((u_short *)dat);
}

static void
l2tp_recv_win_size_print(const u_char *dat, u_int length)
{
	print_short((u_short *)dat); 
}

static void
l2tp_challenge_print(const u_char *dat, u_int length)
{
	print_octets(dat, length);
}

static void
l2tp_q931_cc_print(const u_char *dat, u_int length)
{
	print_short((u_short *)dat);
	print_short((u_short *)(dat + 2));
	if (length > 3) {
		printf(" ");
		print_string(dat+3, length-3);
	} 
}

static void
l2tp_challenge_resp_print(const u_char *dat, u_int length)
{
	print_octets(dat, 16);		/* XXX length should be 16? */
}

static void
l2tp_assnd_sess_id_print(const u_char *dat, u_int length)
{
	print_short((u_short *)dat);
}

static void
l2tp_call_ser_num_print(const u_char *dat, u_int length)
{
	print_int((u_int *)dat);
}

static void
l2tp_minimum_bps_print(const u_char *dat, u_int length)
{
	print_int((u_int *)dat);
}

static void
l2tp_maximum_bps_print(const u_char *dat, u_int length)
{
	print_int((u_int *)dat);
}

static void
l2tp_bearer_type_print(const u_char *dat, u_int length)
{
	u_int *ptr = (u_int *)dat;

	if (ntohl(*ptr) &  L2TP_BEARER_TYPE_ANALOG_MASK) {
		printf("A");
	}
	if (ntohl(*ptr) &  L2TP_BEARER_TYPE_DIGITAL_MASK) {
		printf("D");
	}
}

static void
l2tp_framing_type_print(const u_char *dat, u_int length)
{
	u_int *ptr = (u_int *)dat;

	if (ntohl(*ptr) &  L2TP_FRAMING_TYPE_ASYNC_MASK) {
		printf("A");
	}
	if (ntohl(*ptr) &  L2TP_FRAMING_TYPE_SYNC_MASK) {
		printf("S");
	}
}

static void
l2tp_packet_proc_delay_print(const u_char *dat, u_int length)
{
	printf("obsolete");
}

static void
l2tp_called_number_print(const u_char *dat, u_int length)
{
	print_string(dat, length);
}

static void
l2tp_calling_number_print(const u_char *dat, u_int length)
{
	print_string(dat, length);
}

static void
l2tp_sub_address_print(const u_char *dat, u_int length)
{
	print_string(dat, length);
}

static void
l2tp_tx_conn_speed_print(const u_char *dat, u_int length)
{
	print_int((u_int *)dat);
}

static void
l2tp_phy_channel_id_print(const u_char *dat, u_int length)
{
	print_int((u_int *)dat);
}

static void
l2tp_ini_recv_lcp_print(const u_char *dat, u_int length)
{
	print_octets(dat, length);
}

static void
l2tp_last_sent_lcp_print(const u_char *dat, u_int length)
{
	print_octets(dat, length);
}

static void
l2tp_last_recv_lcp_print(const u_char *dat, u_int length)
{
	print_octets(dat, length);
}

static void
l2tp_proxy_auth_type_print(const u_char *dat, u_int length)
{
	u_short *ptr = (u_short *)dat;

	switch (ntohs(*ptr)) {
	case L2TP_AUTHEN_TYPE_RESERVED:
		printf("Reserved");
		break;
	case L2TP_AUTHEN_TYPE_TEXTUAL:
		printf("Textual");
		break;
	case L2TP_AUTHEN_TYPE_CHAP:
		printf("CHAP");
		break;
	case L2TP_AUTHEN_TYPE_PAP:
		printf("PAP");
		break;
	case L2TP_AUTHEN_TYPE_NO_AUTH:
		printf("No Auth");
		break;
	case L2TP_AUTHEN_TYPE_MSCHAP:
		printf("MS-CHAP");
		break;
	default:
		printf("unknown");
	}
}

static void
l2tp_proxy_auth_name_print(const u_char *dat, u_int length)
{
	print_octets(dat, length);
}

static void
l2tp_proxy_auth_chal_print(const u_char *dat, u_int length)
{
	print_octets(dat, length);
}

static void
l2tp_proxy_auth_id_print(const u_char *dat, u_int length)
{
	u_short *ptr = (u_short *)dat;

	printf("%d", ntohs(*ptr) & L2TP_PROXY_AUTH_ID_MASK);
}

static void
l2tp_proxy_auth_resp_print(const u_char *dat, u_int length)
{
	print_octets(dat, length);
}

static void
l2tp_call_errors_print(const u_char *dat, u_int length)
{
	struct l2tp_call_errors *ptr = (struct l2tp_call_errors *)dat;

	printf("CRCErr=%d FrameErr=%d HardOver=%d BufOver=%d ",
	       ptr->crc_errs,
	       ptr->framing_errs,
	       ptr->hardware_overruns,
	       ptr->buffer_overruns);
	printf("Timeout=%d AlingErr=%d",
	       ptr->timeout_errs,
	       ptr->alignment_errs);
}

static void
l2tp_accm_print(const u_char *dat, u_int length)
{
	struct l2tp_accm *ptr = (struct l2tp_accm *)dat;

	printf("send=%x recv=%x", ptr->send_accm, ptr->recv_accm);
}

static void
l2tp_random_vector_print(const u_char *dat, u_int length)
{
	print_octets(dat, length);
}

static void
l2tp_private_grp_id_print(const u_char *dat, u_int length)
{
	print_string(dat, length);	
	/* XXX print_octets is more appropriate?? */
}

static void
l2tp_rx_conn_speed_print(const u_char *dat, u_int length)
{
	print_int((u_int *)dat);
}

static void
l2tp_seq_required_print(const u_char *dat, u_int length)
{
	return;
}

static void
l2tp_avp_print(const u_char *dat, u_int length)
{
	u_int len;
	const u_short *ptr = (u_short *)dat;
	int hidden = FALSE;

	printf(" ");
	if (length > 0 && (snapend - dat) >= 2) {
		/* there must be at least two octets for the length
		   to be decoded */
		if ((len = (ntohs(*ptr) & L2TP_AVP_HDR_LEN_MASK)) <=
		    (snapend - dat)) {
			if (ntohs(*ptr) & L2TP_AVP_HDR_FLAG_MANDATORY) {
				printf("*");
			}
			if (ntohs(*ptr) & L2TP_AVP_HDR_FLAG_HIDDEN) {
				hidden = TRUE;
				printf("?");
			}
		} else {
			printf("|...");
			return;
		}
		ptr++;

		if (ntohs(*ptr)) {	/* IETF == 0 */
			printf("vendor=%04x", ntohs(*ptr));
		}
		ptr++;

		if (ntohs(*ptr) < L2TP_MAX_AVP_INDEX) {
			printf("%s", l2tp_avp[ntohs(*ptr)].name);
			printf("(");
			if (!hidden) {
				(l2tp_avp[ntohs(*ptr)].print)
					((u_char *)ptr+2, len-6);
			} else {
				printf("???");
			}
			printf(")");
		} else {
			printf(" invalid AVP %u", ntohs(*ptr));
		}

		l2tp_avp_print(dat + len, length - len);
	} else if (length == 0) {
		return;
	} else {
		printf("|...");
	}
}


void
l2tp_print(const u_char *dat, u_int length)
{
	const u_short *ptr = (u_short *)dat;
	u_int cnt = 0;			/* total octets consumed */
	u_short pad;
	int flag_t, flag_l, flag_s, flag_o, flag_p;
	u_short l2tp_len;

	flag_t = flag_l = flag_s = flag_o = flag_p = FALSE;

	if (min(length, snapend - dat) - 6 < 0) { 
		/* flag/ver, tunnel_id, session_id must be present for
		   this packet to be properly decoded */
		printf("%s", tstr);
		return;
	}

	if ((ntohs(*ptr) & L2TP_VERSION_MASK) == L2TP_VERSION_L2TP) {
		printf(" l2tp:");
	} else if ((ntohs(*ptr) & L2TP_VERSION_MASK) == L2TP_VERSION_L2F) {
		printf(" l2f:");
		return;		/* nothing to do */
	} else {
		printf(" Unknown Version, neither L2F(1) nor L2TP(2)");
		return;		/* nothing we can do */
	}

	printf("[");
	if (ntohs(*ptr) & L2TP_FLAG_TYPE) {
		flag_t = TRUE;
		printf("T");
	}
	if (ntohs(*ptr) & L2TP_FLAG_LENGTH) {
		flag_l = TRUE;
		printf("L");
	}
	if (ntohs(*ptr) & L2TP_FLAG_SEQUENCE) {
		flag_s = TRUE;
		printf("S");
	}
	if (ntohs(*ptr) & L2TP_FLAG_OFFSET) {
		flag_o = TRUE;
		printf("O");
	}
	if (ntohs(*ptr) & L2TP_FLAG_PRIORITY) {
		flag_p = TRUE;
		printf("P");
	}
	printf("]");

	ptr++;
	cnt += 2;
	
	if (flag_l) {
		l2tp_len = ntohs(*ptr++);	/* XXX need to consider 
						   truncation ?? */
		cnt += 2;
	} else {
		l2tp_len = 0;
	}

	printf("(%d/", ntohs(*ptr++));		/* Tunnel ID */
	printf("%d)",  ntohs(*ptr++));		/* Session ID */
	cnt += 4;

	if (flag_s) {
		printf("Ns=%d,", ntohs(*ptr++));
		printf("Nr=%d",  ntohs(*ptr++));
		cnt += 4;
	}

	if (flag_o) {
		pad =  ntohs(*ptr++);
		(u_char *)ptr += pad;
		cnt += (2 + pad);
	}

	if (flag_t) {
		if (length - cnt == 0) {
			printf(" ZLB");
		} else {
			l2tp_avp_print((u_char *)ptr, length - cnt);
		}
	} else {
#if 0
		printf(" {");
		ppp_hdlc_print((u_char *)ptr, length - cnt);
		printf("}");
#else
		printf("[hdlc|]");
#endif
	}
}