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

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

Revision 1.36, Wed Dec 1 18:28:46 2021 UTC (2 years, 6 months ago) by deraadt
Branch: MAIN
CVS Tags: OPENBSD_7_5_BASE, OPENBSD_7_5, OPENBSD_7_4_BASE, OPENBSD_7_4, OPENBSD_7_3_BASE, OPENBSD_7_3, OPENBSD_7_2_BASE, OPENBSD_7_2, OPENBSD_7_1_BASE, OPENBSD_7_1, HEAD
Changes since 1.35: +1 -3 lines

we do not need 'struct mbuf; struct rtentry;' to satisfy some ancient unix
variant that made a header file mistake.
ok jsg

/*	$OpenBSD: print-ppp.c,v 1.36 2021/12/01 18:28:46 deraadt Exp $	*/

/*
 * Copyright (c) 1990, 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.
 */

#ifdef PPP
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/file.h>
#include <sys/ioctl.h>

#include <net/if.h>

#include <netinet/in.h>
#include <netinet/ip.h>

#include <ctype.h>
#include <netdb.h>
#include <pcap.h>
#include <signal.h>
#include <stdio.h>

#include <netinet/if_ether.h>
#include "ethertype.h"

#include <net/ppp_defs.h>
#include "interface.h"
#include "addrtoname.h"
#include "extract.h"

#ifndef nitems
#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
#endif

#ifndef PPP_EAP
#define PPP_EAP 0xc227
#endif

#ifndef PPP_CDP
#define PPP_CDP 0x0207
#endif

#ifndef PPP_CDPCP
#define PPP_CDPCP 0x8207
#endif

struct protonames {
	u_short protocol;
	char *name;
};

static const struct protonames protonames[] = {
	/*
	 * Protocol field values.
	 */
	{ PPP_IP,	"IP" },		/* Internet Protocol */
	{ PPP_XNS,	"XNS" },	/* Xerox NS */
	{ PPP_IPX,	"IPX" },	/* IPX Datagram (RFC1552) */
	{ PPP_AT,	"AppleTalk" },	/* AppleTalk Protocol */
	{ PPP_VJC_COMP,	"VJC_UNCOMP" },	/* VJ compressed TCP */
	{ PPP_VJC_UNCOMP,"VJC_UNCOMP" },/* VJ uncompressed TCP */
	{ PPP_IPV6,	"IPv6" },	/* Internet Protocol version 6 */
	{ PPP_COMP,	"COMP" },	/* compressed packet */
	{ PPP_IPCP,	"IPCP" },	/* IP Control Protocol */
	{ PPP_ATCP,	"AppleTalkCP" },/* AppleTalk Control Protocol */
	{ PPP_IPXCP,	"IPXCP" },	/* IPX Control Protocol (RFC1552) */
	{ PPP_IPV6CP,	"IPV6CP" },	/* IPv6 Control Protocol */
	{ PPP_CCP,	"CCP" },	/* Compression Control Protocol */
	{ PPP_LCP,	"LCP" },	/* Link Control Protocol */
	{ PPP_PAP,	"PAP" },	/* Password Authentication Protocol */
	{ PPP_LQR,	"LQR" },	/* Link Quality Report protocol */
	{ PPP_CBCP,	"CBCP" },	/* Callback Control Protocol */
	{ PPP_CHAP,	"CHAP" },	/* Cryptographic Handshake Auth. Proto */
	{ PPP_EAP,	"EAP" },	/* Extensible Auth. Protocol */
	{ PPP_CDP,	"CDP" },
	{ PPP_CDPCP,	"CDPCP" },
};

struct ppp_control {
	uint8_t		code;
	uint8_t		id;
	uint16_t	len;
};

struct ppp_cp_type {
	const char	 *unkname;
	int		  mincode;
	int		  maxcode;
	const char 	**codes;
};

/* LCP */

#define LCP_CONF_REQ	1
#define LCP_CONF_ACK	2
#define LCP_CONF_NAK	3
#define LCP_CONF_REJ	4
#define LCP_TERM_REQ	5
#define LCP_TERM_ACK	6
#define LCP_CODE_REJ	7
#define LCP_PROT_REJ	8
#define LCP_ECHO_REQ	9
#define LCP_ECHO_RPL	10
#define LCP_DISC_REQ	11

#define LCP_MIN	LCP_CONF_REQ
#define LCP_MAX LCP_DISC_REQ

static const char *lcpcodes[] = {
	/*
	 * LCP code values (RFC1661, pp26)
	 */
	"Configure-Request",
	"Configure-Ack",
	"Configure-Nak",
	"Configure-Reject",
	"Terminate-Request",
	"Terminate-Ack",
 	"Code-Reject",
	"Protocol-Reject",
	"Echo-Request",
	"Echo-Reply",
	"Discard-Request",
};

#define LCPOPT_VEXT	0
#define LCPOPT_MRU	1
#define LCPOPT_ACCM	2
#define LCPOPT_AP	3
#define LCPOPT_QP	4
#define LCPOPT_MN	5
#define LCPOPT_PFC	7
#define LCPOPT_ACFC	8

static char *lcpconfopts[] = {
	"Vendor-Ext",
	"Max-Rx-Unit",
	"Async-Ctrl-Char-Map",
	"Auth-Prot",
	"Quality-Prot",
	"Magic-Number",
	"unassigned (6)",	
	"Prot-Field-Compr",
	"Add-Ctrl-Field-Compr",
	"FCS-Alternatives",
	"Self-Describing-Pad",
	"Numbered-Mode",
	"Multi-Link-Procedure",
	"Call-Back",
	"Connect-Time"
	"Compund-Frames",
	"Nominal-Data-Encap",
	"Multilink-MRRU",
	"Multilink-SSNHF",
	"Multilink-ED",
	"Proprietary",
	"DCE-Identifier",
	"Multilink-Plus-Proc",
	"Link-Discriminator",
	"LCP-Auth-Option",
};

/* CHAP */

#define CHAP_CHAL	1
#define CHAP_RESP	2
#define CHAP_SUCC	3
#define CHAP_FAIL	4

#define CHAP_CODEMIN 1
#define CHAP_CODEMAX 4

static const char *chapcode[] = {
	"Challenge",
	"Response",
	"Success",
	"Failure",	
};

/* PAP */

#define PAP_AREQ	1
#define PAP_AACK	2
#define PAP_ANAK	3

#define PAP_CODEMIN	1
#define PAP_CODEMAX	3

static const char *papcode[] = {
	"Authenticate-Request",
	"Authenticate-Ack",
	"Authenticate-Nak",
};

/* EAP */

#define EAP_CHAL	1
#define EAP_RESP	2
#define EAP_SUCC	3
#define EAP_FAIL	4

#define EAP_CODEMIN	EAP_CHAL
#define EAP_CODEMAX	EAP_FAIL

#define EAP_TYPE_IDENTITY	1
#define EAP_TYPE_NOTIFICATION	2
#define EAP_TYPE_NAK		3
#define EAP_TYPE_MD5_CHALLENGE	4
#define EAP_TYPE_OTP		5
#define EAP_TYPE_TOKEN		6

#define EAP_TYPEMIN		EAP_TYPE_IDENTITY
#define EAP_TYPEMAX		EAP_TYPE_TOKEN

static const char *eapcode[] = {
	"Challenge",
	"Response",
	"Success",
	"Failure",	
};

static const char *eaptype[] = {
	"Identity",
	"Notification",
	"Nak",
	"MD5-Challenge",
	"One-Time-Password",
	"Token",
};


/* IPCP */

#define IPCP_CODE_CFG_REQ	1
#define IPCP_CODE_CFG_ACK	2
#define IPCP_CODE_CFG_NAK	3
#define IPCP_CODE_CFG_REJ	4
#define IPCP_CODE_TRM_REQ	5
#define IPCP_CODE_TRM_ACK	6
#define IPCP_CODE_COD_REJ	7

#define IPCP_CODE_MIN IPCP_CODE_CFG_REQ
#define IPCP_CODE_MAX IPCP_CODE_COD_REJ

#define IPCP_2ADDR	1
#define IPCP_CP		2
#define IPCP_ADDR	3

/* IPV6CP */

#define IPV6CP_CODE_CFG_REQ	1
#define IPV6CP_CODE_CFG_ACK	2
#define IPV6CP_CODE_CFG_NAK	3
#define IPV6CP_CODE_CFG_REJ	4
#define IPV6CP_CODE_TRM_REQ	5
#define IPV6CP_CODE_TRM_ACK	6
#define IPV6CP_CODE_COD_REJ	7

#define IPV6CP_CODE_MIN IPV6CP_CODE_CFG_REQ
#define IPV6CP_CODE_MAX IPV6CP_CODE_COD_REJ

#define IPV6CP_IFID	1

static int print_lcp_config_options(const u_char *p, int);
static void handle_lcp(const u_char *, int);
static void handle_chap(const u_char *p, int);
static void handle_eap(const u_char *p, int);
static void handle_ipcp(const u_char *p, int);
static int print_ipcp_config_options(const u_char *, int);
static void handle_ipv6cp(const u_char *p, int);
static int print_ipv6cp_config_options(const u_char *, int);
static void handle_pap(const u_char *p, int);

struct pppoe_header {
	u_int8_t vertype;	/* PPPoE version/type */
	u_int8_t code;		/* PPPoE code (packet type) */
	u_int16_t sessionid;	/* PPPoE session id */
	u_int16_t len;		/* PPPoE payload length */
};
#define	PPPOE_CODE_SESSION	0x00	/* Session */
#define	PPPOE_CODE_PADO		0x07	/* Active Discovery Offer */
#define	PPPOE_CODE_PADI		0x09	/* Active Discovery Initiation */
#define	PPPOE_CODE_PADR		0x19	/* Active Discovery Request */
#define	PPPOE_CODE_PADS		0x65	/* Active Discovery Session-Confirm */
#define	PPPOE_CODE_PADT		0xa7	/* Active Discovery Terminate */
#define	PPPOE_TAG_END_OF_LIST		0x0000	/* End Of List */
#define	PPPOE_TAG_SERVICE_NAME		0x0101	/* Service Name */
#define	PPPOE_TAG_AC_NAME		0x0102	/* Access Concentrator Name */
#define	PPPOE_TAG_HOST_UNIQ		0x0103	/* Host Uniq */
#define	PPPOE_TAG_AC_COOKIE		0x0104	/* Access Concentratr Cookie */
#define	PPPOE_TAG_VENDOR_SPEC		0x0105	/* Vendor Specific */
#define	PPPOE_TAG_RELAY_SESSION		0x0110	/* Relay Session Id */
#define	PPPOE_TAG_MAX_PAYLOAD		0x0120	/* RFC 4638 Max Payload */
#define	PPPOE_TAG_SERVICE_NAME_ERROR	0x0201	/* Service Name Error */
#define	PPPOE_TAG_AC_SYSTEM_ERROR	0x0202	/* Acc. Concentrator Error */
#define	PPPOE_TAG_GENERIC_ERROR		0x0203	/* Generic Error */

static void
ppp_protoname(uint16_t proto)
{
	const struct protonames *protoname;
	int i;

	/* bsearch? */
	for (i = 0; i < nitems(protonames); i++) {
		protoname = &protonames[i];

		if (proto == protoname->protocol) {
			printf("%s ", protoname->name);
			return;
		}
	}

	printf("unknown-ppp-%04x", proto);
}

void
ppp_print(const u_char *p, u_int length)
{
	uint16_t proto;
	int l;

	l = snapend - p;

	if (l < sizeof(proto)) {
		printf("[|ppp]");
		return;
	}

	proto = EXTRACT_16BITS(p);

	p += sizeof(proto);
	l -= sizeof(proto);
	length -= sizeof(proto);

	if (eflag)
		ppp_protoname(proto);

	switch (proto) {
	case PPP_IP:
		ip_print(p, length);
		return;
	case PPP_IPV6:
		ip6_print(p, length);
		return;
	}

	if (!eflag)
		ppp_protoname(proto);

	switch(proto) {
	case PPP_LCP:
		handle_lcp(p, l);
		break;
	case PPP_CHAP:
		handle_chap(p, l);
		break;
	case PPP_EAP:
		handle_eap(p, l);
		break;
	case PPP_PAP:
		handle_pap(p, l);
		break;
	case PPP_IPCP:
		handle_ipcp(p, l);
		break;
	case PPP_IPV6CP:
		handle_ipv6cp(p, l);
		break;
	case PPP_CDP:
		cdp_print(p, length, l, 0);
		break;
	}
}

static int
ppp_cp_header(struct ppp_control *pc, const u_char *p, int l,
    const struct ppp_cp_type *t)
{
	uint8_t code;
	int off = 0;
	int len;

	len = sizeof(pc->code);
	if (l < len)
		return (-1);

	pc->code = code = *(p + off);
	if (code >= t->mincode && code <= t->maxcode)
		printf("%s ", t->codes[code - 1]);
	else
		printf("unknown-%s-%u ", t->unkname, pc->code);

	off = len;
	len += sizeof(pc->id);
	if (l < len)
		return (-1);

	pc->id = *(p + off);
	printf("Id=0x%02x:", pc->id);

	off = len;
	len += sizeof(pc->len);
	if (l < len)
		return (-1);

	pc->len = EXTRACT_16BITS(p + off);

	return (len);
}

/* print LCP frame */

static const struct ppp_cp_type ppp_cp_lcp = {
	"lcp",
	LCP_MIN, LCP_MAX,
	lcpcodes,
};

static void
handle_lcp(const u_char *p, int l)
{
	struct ppp_control pc;
	int i;

	if (ppp_cp_header(&pc, p, l, &ppp_cp_lcp) == -1)
		goto trunc;

	if (l > pc.len)
		l = pc.len;

	p += sizeof(pc);
	l -= sizeof(pc);

	switch (pc.code) {
	case LCP_CONF_REQ:
	case LCP_CONF_ACK:
	case LCP_CONF_NAK:
	case LCP_CONF_REJ:
		while (l > 0) {
			int optlen;
	
			optlen = print_lcp_config_options(p, l);
			if (optlen == -1)
				goto trunc;
			if (optlen == 0)
				break;

			p += optlen;
			l -= optlen;
		}
		break;
	case LCP_ECHO_REQ:
	case LCP_ECHO_RPL:
		if (l < 4)
			goto trunc;
		printf(" Magic-Number=%u", EXTRACT_32BITS(p));
		p += 4;
		l -= 4;

		i = sizeof(pc) + 4;
		if (i == pc.len)
			break;

		printf(" Data=");
		do {
			if (l == 0)
				goto trunc;

			printf("%02x", *p);

			p++;
			l--;
		} while (++i < pc.len);
		break;
	case LCP_TERM_REQ:
	case LCP_TERM_ACK:
	case LCP_CODE_REJ:
	case LCP_PROT_REJ:
	case LCP_DISC_REQ:
	default:
		break;
	}
	return;

trunc:
	printf("[|lcp]");
}

/* LCP config options */

static int
print_lcp_config_options(const u_char *p, int l)
{
	uint8_t type, length;
	uint16_t proto;

	if (l < sizeof(type))
		return (-1);

	type = p[0];
	if (type < nitems(lcpconfopts))
		printf(" %s", lcpconfopts[type]);
	else
		printf(" unknown-lcp-%u", type);

	if (l < sizeof(type) + sizeof(length))
		return (-1);

	length = p[1];

	if (length < sizeof(type) + sizeof(length))
		return (0);

	if (l > length)
		l = length;

	p += sizeof(type) + sizeof(length);
	l -= sizeof(type) + sizeof(length);

	switch (type) {
	case LCPOPT_MRU:
		if (length != 4)
			goto invalid;
		if (l < 2)
			return (-1);

		printf("=%u", EXTRACT_16BITS(p));
		break;
	case LCPOPT_AP:
		if (length < 4)
			goto invalid;
		if (l < sizeof(proto))
			return (-1);

		proto = EXTRACT_16BITS(p);
		switch (proto) {
		case PPP_PAP:
			printf("=PAP");
			break;
		case PPP_CHAP:
			printf("=CHAP");
			if (length < 5)
				goto invalid;

			p += sizeof(proto);
			l -= sizeof(proto);
	
			type = *p;
			switch (type) {
			case 0x05:
				printf("/MD5");
				break;
			case 0x80:
				printf("/Microsoft");
				break;
			default:
				printf("/unknown-algorithm-%02x", type);
				break;
			}
			break;
		case PPP_EAP:
			printf("=EAP");
			break;
		case 0xc027:
			printf("=SPAP");
			break;
		case 0xc127:
			printf("=Old-SPAP");
			break;
		default:
			printf("=unknown-ap-%04x", proto);
			break;
		}
		break;
	case LCPOPT_QP:
		if (length < 4)
			goto invalid;
		if (l < sizeof(proto))
			return (-1);

		proto = EXTRACT_16BITS(p);
		switch (proto) {
		case PPP_LQR:
			printf(" LQR");
			break;
		default:
			printf(" unknown-qp-%u", proto);
		}
		break;
	case LCPOPT_MN:
		if (length < 6)
			goto invalid;
		if (l < 4)
			return (-1);

		printf("=%u", EXTRACT_32BITS(p));
		break;
	case LCPOPT_PFC:
		printf(" PFC");
		break;
	case LCPOPT_ACFC:
		printf(" ACFC");
		break;
	}

	return (length);

invalid:
	printf(" invalid opt len %u", length);
	return (length);
}

/* CHAP */

static const struct ppp_cp_type ppp_cp_chap = {
	"chap",
	CHAP_CODEMIN, CHAP_CODEMAX,
	chapcode,
};

static void
handle_chap(const u_char *p, int l)
{
	struct ppp_control pc;
	uint8_t vsize;
	int i;

	if (ppp_cp_header(&pc, p, l, &ppp_cp_chap) == -1)
		goto trunc;

	if (l > pc.len)
		l = pc.len;

	p += sizeof(pc);
	l -= sizeof(pc);

	switch (pc.code) {
	case CHAP_CHAL:
	case CHAP_RESP:
		if (l < sizeof(vsize))
			goto trunc;

		vsize = *p;
		if (vsize < 1) {
			printf(" invalid Value-Size");
			return;
		}

		p += sizeof(vsize);
		l -= sizeof(vsize);

		printf(" Value=");
		for (i = 0; i < vsize; i++) {
			if (l == 0)
				goto trunc;

			printf("%02x", *p);

			p++;
			l--;
		}

		printf(" Name=");
		for (i += sizeof(pc) + sizeof(vsize); i < pc.len; i++) {
			if (l == 0)
				goto trunc;

			safeputchar(*p);

			p++;
			l--;
		}
		break;
	case CHAP_SUCC:
	case CHAP_FAIL:
		printf(" Message=");
		for (i = sizeof(pc); i < pc.len; i++) {
			if (l == 0)
				goto trunc;

			safeputchar(*p);

			p++;
			l--;
		}
		break;
	}
	return;

trunc:
	printf("[|chap]");
}

/* EAP */

static const struct ppp_cp_type ppp_cp_eap = {
	"eap",
	EAP_CODEMIN, EAP_CODEMAX,
	eapcode,
};

static void
handle_eap(const u_char *p, int l)
{
	struct ppp_control pc;
	uint8_t type, vsize;
	int i;

	if (ppp_cp_header(&pc, p, l, &ppp_cp_eap) == -1)
		goto trunc;

	if (l > pc.len)
		l = pc.len;

	p += sizeof(pc);
	l -= sizeof(pc);

	switch (pc.code) {
	case EAP_CHAL:
	case EAP_RESP:
		if (l < sizeof(type))
			goto trunc;

		type = *p;
		p += sizeof(type);
		l -= sizeof(type);

		if (type >= EAP_TYPEMIN && type <= EAP_TYPEMAX)
			printf(" %s", eaptype[type - 1]);
		else {
			printf(" unknown-eap-type-%u", type);
			return;
		}

		switch (type) {
		case EAP_TYPE_IDENTITY:
		case EAP_TYPE_NOTIFICATION:
		case EAP_TYPE_OTP:
			i = sizeof(pc) + sizeof(type);
			if (i == pc.len)
				break;

			printf("=");
			do {
				if (l == 0)
					goto trunc;

				safeputchar(*p);

				p++;
				l--;
			} while (++i < pc.len);
			break;

		case EAP_TYPE_NAK:
			if (l < sizeof(type))
				goto trunc;
			type = *p;
			if (type >= EAP_TYPEMIN && type <= EAP_TYPEMAX)
				printf(" %s", eaptype[type - 1]);
			else
				printf(" unknown-eap-type-%u", type);
			break;
		case EAP_TYPE_MD5_CHALLENGE:
			if (l < sizeof(vsize))
				goto trunc;

			vsize = *p;
			p += sizeof(vsize);
			l -= sizeof(vsize);

			printf("=");
			for (i = 0; i < vsize; i++) {
				if (l == 0)
					goto trunc;

				printf("%02x", *p);

				p++;
				l--;
			}
			break;
		}
		break;
	case CHAP_SUCC:
	case CHAP_FAIL:
		break;
	}
	return;

trunc:
	printf("[|eap]");
}

/* PAP */

static const struct ppp_cp_type ppp_cp_pap = {
	"pap",
	PAP_CODEMIN, PAP_CODEMAX,
	papcode,
};

static void
handle_pap(const u_char *p, int l)
{
	struct ppp_control pc;
	uint8_t x;
	int i;

	if (ppp_cp_header(&pc, p, l, &ppp_cp_pap) == -1)
		goto trunc;

	if (l > pc.len)
		l = pc.len;

	p += sizeof(pc);
	l -= sizeof(pc);

	switch (pc.code) {
	case PAP_AREQ:
		if (l < sizeof(x)) /* Peer-ID Length */
			goto trunc;

		x = *p;

		p += sizeof(x);
		l -= sizeof(x);
	
		printf(" Peer-Id=");
		for (i = 0; i < x; i++) {
			if (l == 0)
				goto trunc;

			safeputchar(*p);

			p++;
			l--;
		}

		if (l < sizeof(x)) /* Passwd-Length */
			goto trunc;

		x = *p;

		p += sizeof(x);
		l -= sizeof(x);
	
		printf(" Passwd=");
		for (i = 0; i < x; i++) {
			if (l == 0)
				goto trunc;

			safeputchar(*p);

			p++;
			l--;
		}
		break;

	case PAP_AACK:
	case PAP_ANAK:
		if (l < sizeof(x)) /* Msg-Length */
			goto trunc;

		x = *p;

		p += sizeof(x);
		l -= sizeof(x);
	
		printf(" Message=");
		for (i = 0; i < x; i++) {
			if (l == 0)
				goto trunc;

			safeputchar(*p);

			p++;
			l--;
		}
		break;
	}

	return;

trunc:
	printf("[|pap]");
}

/* IPCP */

#define IP_LEN 4
#define IP_FMT "%u.%u.%u.%u"
#define IP_ARG(_p) (_p)[0], (_p)[1], (_p)[2], (_p)[3]

static const struct ppp_cp_type ppp_cp_ipcp = {
	"ipcp",
	IPCP_CODE_MIN, IPCP_CODE_MAX,
	lcpcodes,
};

static void
handle_ipcp(const u_char *p, int l)
{
	struct ppp_control pc;

	if (ppp_cp_header(&pc, p, l, &ppp_cp_ipcp) == -1)
		goto trunc;

	if (l > pc.len)
		l = pc.len;

	p += sizeof(pc);
	l -= sizeof(pc);

	switch (pc.code) {
	case IPCP_CODE_CFG_REQ:
	case IPCP_CODE_CFG_ACK:
	case IPCP_CODE_CFG_NAK:
	case IPCP_CODE_CFG_REJ:
		while (l > 0) {
			int optlen;
	
			optlen = print_ipcp_config_options(p, l);
			if (optlen == -1)
				goto trunc;
			if (optlen == 0)
				break;

			p += optlen;
			l -= optlen;
		}
		break;

	case IPCP_CODE_TRM_REQ:
	case IPCP_CODE_TRM_ACK:
	case IPCP_CODE_COD_REJ:
	default:
		break;
	}

	return;

trunc:
	printf("[|ipcp]");
}

static int
print_ipcp_config_options(const u_char *p, int l)
{
	uint8_t type, length;

	if (l < sizeof(type))
		return (-1);

	type = p[0];
	switch (type) {
	case IPCP_2ADDR:
		printf(" IP-Addresses");
		break;
	case IPCP_CP:
		printf(" IP-Compression-Protocol");
		break;
	case IPCP_ADDR:
		printf(" IP-Address");
		break;
	default:
		printf(" ipcp-type-%u", type);
		break;
	}

	if (l < sizeof(type) + sizeof(length))
		return (-1);

	length = p[1];

	p += (sizeof(type) + sizeof(length));
	l -= (sizeof(type) + sizeof(length));

	switch (type) {
	case IPCP_2ADDR:
		if (length != 10)
			goto invalid;
		if (l < IP_LEN)
			return (-1);

		printf(" Src=" IP_FMT, IP_ARG(p));

		p += IP_LEN;
		l -= IP_LEN;

		if (l < IP_LEN)
			return (-1);

		printf(" Dst=" IP_FMT, IP_ARG(p));
		break;
	case IPCP_CP:
		if (length < 4)
			goto invalid;
		if (l < sizeof(type))
			return (-1);

		type = EXTRACT_16BITS(p);
		switch (type) {
		case 0x0037:
			printf(" Van Jacobsen Compressed TCP/IP");
			break;
		default:
			printf("ipcp-compression-type-%u", type);
			break;
		}
		break;
	case IPCP_ADDR:
		if (length != 6)
			goto invalid;
		if (l < IP_LEN)
			return (-1);

		printf("=" IP_FMT, IP_ARG(p));
		break;
	}

	return (length);

invalid:
	printf(" invalid opt len %u", length);
	return (length);
}

/* IPV6CP */

static const struct ppp_cp_type ppp_cp_ipv6cp = {
	"ipv6cp",
	IPV6CP_CODE_MIN, IPV6CP_CODE_MAX,
	lcpcodes,
};

static void
handle_ipv6cp(const u_char *p, int l)
{
	struct ppp_control pc;

	if (ppp_cp_header(&pc, p, l, &ppp_cp_ipv6cp) == -1)
		goto trunc;

	if (l > pc.len)
		l = pc.len;

	p += sizeof(pc);
	l -= sizeof(pc);

	switch (pc.code) {
	case IPV6CP_CODE_CFG_REQ:
	case IPV6CP_CODE_CFG_ACK:
	case IPV6CP_CODE_CFG_NAK:
	case IPV6CP_CODE_CFG_REJ:
		while (l > 0) {
			int optlen;
	
			optlen = print_ipv6cp_config_options(p, l);
			if (optlen == -1)
				goto trunc;
			if (optlen == 0)
				break;

			p += optlen;
			l -= optlen;
		}
		break;

	case IPV6CP_CODE_TRM_REQ:
	case IPV6CP_CODE_TRM_ACK:
	case IPV6CP_CODE_COD_REJ:
	default:
		break;
	}

	return;

trunc:
	printf("[|ipv6cp]");
}

static int
print_ipv6cp_config_options(const u_char *p, int l)
{
	uint8_t type, length;

	if (l < sizeof(type))
		return (-1);

	type = p[0];
	switch (type) {
	case IPV6CP_IFID:
		printf(" IPv6-Interface-Id");
		break;
	default:
		printf(" ipv6cp-type-%u", type);
		break;
	}

	if (l < sizeof(type) + sizeof(length))
		return (-1);

	length = p[1];

	p += (sizeof(type) + sizeof(length));
	l -= (sizeof(type) + sizeof(length));

	switch (type) {
	case IPV6CP_IFID:
		if (length != 10)
			goto invalid;
		if (l < 8)
			return (-1);

		printf("=%04x:%04x:%04x:%04x", EXTRACT_16BITS(p + 0),
		    EXTRACT_16BITS(p + 2), EXTRACT_16BITS(p + 4), 
		    EXTRACT_16BITS(p + 6));
		break;
	default:
		break;
	}

	return (length);
invalid:
	printf(" invalid opt len %u", length);
	return (length);
}

void
ppp_if_print(u_char *user, const struct pcap_pkthdr *h, const u_char *p)
{
	u_int length = h->len;
	u_int caplen = h->caplen;

	packetp = p;
	snapend = p + caplen;

	ts_print(&h->ts);

	ppp_hdlc_print(p, length);

	if (xflag)
		default_print((const u_char *)(p + PPP_HDRLEN),
		    caplen - PPP_HDRLEN);

	putchar('\n');
}

void
ppp_ether_if_print(u_char *user, const struct pcap_pkthdr *h, const u_char *p)
{
	u_int16_t pppoe_sid, pppoe_len;
	u_int l = h->caplen;
	u_int length = h->len;

	packetp = p;
	snapend = p + l;

	ts_print(&h->ts);

	if (eflag)
		printf("PPPoE ");

	if (l < sizeof(struct pppoe_header)) {
		printf("[|pppoe]");
		return;
	}

	pppoe_sid = EXTRACT_16BITS(p + 2);
	pppoe_len = EXTRACT_16BITS(p + 4);

	if (eflag) {
		printf("\n\tcode ");
		switch (p[1]) {
		case PPPOE_CODE_PADI:
			printf("Initiation");
			break;
		case PPPOE_CODE_PADO:
			printf("Offer");
			break;
		case PPPOE_CODE_PADR:
			printf("Request");
			break;
		case PPPOE_CODE_PADS:
			printf("Confirm");
			break;
		case PPPOE_CODE_PADT:
			printf("Terminate");
			break;
		case PPPOE_CODE_SESSION:
			printf("Session");
			break;
		default:
			printf("Unknown(0x%02x)", p[1]);
			break;
		}
		printf(", version %d, type %d, id 0x%04x, length %d\n\t",
		    (p[0] & 0xf), (p[0] & 0xf0) >> 4, pppoe_sid, pppoe_len);
	}

	if (length < pppoe_len) {
                printf(" truncated-pppoe - %d bytes missing!",
                    pppoe_len - length);
                pppoe_len = length;
        }

	ppp_print(p + sizeof(struct pppoe_header), pppoe_len);

	if (xflag)
		default_print(p, h->caplen);

	putchar('\n');
}

int
pppoe_if_print(u_short ethertype, const u_char *p, u_int length, u_int l)
{
	uint16_t pppoe_sid, pppoe_len;

	if (ethertype == ETHERTYPE_PPPOEDISC)
		printf("PPPoE-Discovery");
	else
		printf("PPPoE-Session");

	if (l < sizeof(struct pppoe_header))
		goto trunc;

	printf("\n\tcode ");
	switch (p[1]) {
	case PPPOE_CODE_PADI:
		printf("Initiation");
		break;
	case PPPOE_CODE_PADO:
		printf("Offer");
		break;
	case PPPOE_CODE_PADR:
		printf("Request");
		break;
	case PPPOE_CODE_PADS:
		printf("Confirm");
		break;
	case PPPOE_CODE_PADT:
		printf("Terminate");
		break;
	case PPPOE_CODE_SESSION:
		printf("Session");
		break;
	default:
		printf("Unknown(0x%02x)", p[1]);
		break;
	}

	pppoe_sid = EXTRACT_16BITS(p + 2);
	pppoe_len = EXTRACT_16BITS(p + 4);
	printf(", version %d, type %d, id 0x%04x, length %d",
	    (p[0] & 0xf), (p[0] & 0xf0) >> 4, pppoe_sid, pppoe_len);

	p += sizeof(struct pppoe_header);
	l -= sizeof(struct pppoe_header);
	length -= sizeof(struct pppoe_header);

	if (length < pppoe_len) {
                printf(" truncated-pppoe - %d bytes missing!",
                    pppoe_len - length);
                pppoe_len = length;
        }

	if (l > pppoe_len)
		l = pppoe_len;

	if (ethertype == ETHERTYPE_PPPOEDISC) {
		while (l > 0) {
			u_int16_t t_type, t_len;

			if (l < 4)
				goto trunc;
			t_type = EXTRACT_16BITS(p);
			t_len = EXTRACT_16BITS(p + 2);

			p += 4;
			l -= 4;

			if (l < t_len)
				goto trunc;

			printf("\n\ttag ");
			switch (t_type) {
			case PPPOE_TAG_END_OF_LIST:
				printf("End-Of-List");
				break;
			case PPPOE_TAG_SERVICE_NAME:
				printf("Service-Name");
				break;
			case PPPOE_TAG_AC_NAME:
				printf("AC-Name");
				break;
			case PPPOE_TAG_HOST_UNIQ:
				printf("Host-Uniq");
				break;
			case PPPOE_TAG_AC_COOKIE:
				printf("AC-Cookie");
				break;
			case PPPOE_TAG_VENDOR_SPEC:
				printf("Vendor-Specific");
				break;
			case PPPOE_TAG_RELAY_SESSION:
				printf("Relay-Session");
				break;
			case PPPOE_TAG_MAX_PAYLOAD:
				printf("PPP-Max-Payload");
				break;
			case PPPOE_TAG_SERVICE_NAME_ERROR:
				printf("Service-Name-Error");
				break;
			case PPPOE_TAG_AC_SYSTEM_ERROR:
				printf("AC-System-Error");
				break;
			case PPPOE_TAG_GENERIC_ERROR:
				printf("Generic-Error");
				break;
			default:
				printf("Unknown(0x%04x)", t_type);
			}
			printf(", length %u%s", t_len, t_len ? " " : "");

			if (t_len) {
				for (t_type = 0; t_type < t_len; t_type++) {
					if (isprint(p[t_type]))
						printf("%c", p[t_type]);
					else
						printf("\\%03o", p[t_type]);
				}
			}
			p += t_len;
			l -= t_len;
		}
	} else if (ethertype == ETHERTYPE_PPPOE) {
		printf("\n\t");
		ppp_print(p, pppoe_len);
	}

	return (1);

trunc:
	printf("[|pppoe]");
	return (1);
}

void
ppp_hdlc_print(const u_char *p, u_int length)
{
	uint8_t address, control;
	int l;

	l = snapend - p;

	if (l < sizeof(address) + sizeof(control))
		goto trunc;

	address = p[0];
	control = p[1];

	p += sizeof(address) + sizeof(control);
	l -= sizeof(address) + sizeof(control);
	length -= sizeof(address) + sizeof(control);

	switch (address) {
	case 0xff: /* All-Stations */
		if (eflag)
			printf("%02x %02x %u ", address, control, length);

		if (control != 0x3) {
			printf(" discard");
			break;
		}

		ppp_print(p, length);
		break;

	default:
		printf("ppp address 0x%02x unknown", address);
		break;
	}
	return;

trunc:
	printf("[|ppp]");
}

void
ppp_hdlc_if_print(u_char *user, const struct pcap_pkthdr *h,
    const u_char *p)
{
	int l = h->caplen;

	packetp = p;
	snapend = p + l;

	ts_print(&h->ts);

	if (eflag)
		printf("PPP ");

	ppp_hdlc_print(p, h->len);

	if (xflag)
		default_print(p, l);

	printf("\n");
}

#else

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

#include <stdio.h>

#include "interface.h"
void
ppp_if_print(user, h, p)
	u_char *user;
	const struct pcap_pkthdr *h;
	const u_char *p;
{
	error("not configured for ppp");
	/* NOTREACHED */
}
#endif