root/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ppp.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2001-2002 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/sysmacros.h>
#include <net/ppp_defs.h>
#include <net/ppp-comp.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <arpa/inet.h>
#include "snoop.h"
#include "snoop_ppp.h"

static int interpret_ppp_cp(int, uchar_t *, int, ppp_protoinfo_t *);
static int interpret_cp_options(uchar_t *, int, ppp_protoinfo_t *);
static int interpret_ppp_chap(int, uchar_t *, int, ppp_protoinfo_t *);
static int interpret_ppp_pap(int, uchar_t *, int, ppp_protoinfo_t *);
static int interpret_ppp_lqr(int, uchar_t *, int, ppp_protoinfo_t *);
static ppp_protoinfo_t *ppp_getprotoinfo(uint16_t);
static cp_optinfo_t *ppp_getoptinfo(cp_optinfo_t *, uint16_t);
static optformat_func_t opt_format_vendor;
static optformat_func_t opt_format_mru;
static optformat_func_t opt_format_accm;
static optformat_func_t opt_format_authproto;
static optformat_func_t opt_format_qualproto;
static optformat_func_t opt_format_magicnum;
static optformat_func_t opt_format_fcs;
static optformat_func_t opt_format_sdp;
static optformat_func_t opt_format_nummode;
static optformat_func_t opt_format_callback;
static optformat_func_t opt_format_mrru;
static optformat_func_t opt_format_epdisc;
static optformat_func_t opt_format_dce;
static optformat_func_t opt_format_linkdisc;
static optformat_func_t opt_format_i18n;
static optformat_func_t opt_format_ipaddresses;
static optformat_func_t opt_format_ipcompproto;
static optformat_func_t opt_format_ipaddress;
static optformat_func_t opt_format_mobileipv4;
static optformat_func_t opt_format_ifaceid;
static optformat_func_t opt_format_ipv6compproto;
static optformat_func_t opt_format_compoui;
static optformat_func_t opt_format_bsdcomp;
static optformat_func_t opt_format_staclzs;
static optformat_func_t opt_format_mppc;
static optformat_func_t opt_format_gandalf;
static optformat_func_t opt_format_lzsdcp;
static optformat_func_t opt_format_magnalink;
static optformat_func_t opt_format_deflate;
static optformat_func_t opt_format_encroui;
static optformat_func_t opt_format_dese;
static optformat_func_t opt_format_muxpid;

/*
 * Many strings below are initialized with "Unknown".
 */
static char unknown_string[] = "Unknown";

/*
 * Each known PPP protocol has an associated ppp_protoinfo_t in this array.
 * Even if we can't decode the protocol (interpret_proto() == NULL),
 * interpret_ppp() will at least print the protocol's name.  There is no
 * dependency on the ordering of the entries in this array.  They have been
 * ordered such that the most commonly used protocols are near the front.
 * The array is delimited by a last entry of protocol of type
 * PPP_PROTO_UNKNOWN.
 */
static ppp_protoinfo_t protoinfo_array[] = {
        { PPP_IP,       "IP",           interpret_ip,   NULL,   NULL },
        { PPP_IPV6,     "IPv6",         interpret_ipv6, NULL,   NULL },
        { PPP_COMP,     "Compressed Data",      NULL,   NULL,   NULL },
        { PPP_OSI,      "OSI",                  NULL,   NULL,   NULL },
        { PPP_AT,       "AppleTalk",            NULL,   NULL,   NULL },
        { PPP_IPX,      "IPX",                  NULL,   NULL,   NULL },
        { PPP_VJC_COMP, "VJ Compressed TCP",    NULL,   NULL,   NULL },
        { PPP_VJC_UNCOMP, "VJ Uncompressed TCP", NULL,  NULL,   NULL },
        { PPP_BRIDGE,   "Bridging",             NULL,   NULL,   NULL },
        { PPP_802HELLO, "802.1d Hello",         NULL,   NULL,   NULL },
        { PPP_MP,       "MP",                   NULL,   NULL,   NULL },
        { PPP_ENCRYPT,  "Encryption",           NULL,   NULL,   NULL },
        { PPP_ENCRYPTFRAG, "Individual Link Encryption", NULL,  NULL,   NULL },
        { PPP_MUX,      "PPP Muxing",           NULL,   NULL,   NULL },
        { PPP_COMPFRAG, "Single Link Compressed Data",  NULL,   NULL,   NULL },
        { PPP_FULLHDR,  "IP Compression",       NULL,   NULL,   NULL },
        { PPP_COMPTCP,  "IP Compression",       NULL,   NULL,   NULL },
        { PPP_COMPNONTCP, "IP Compression",     NULL,   NULL,   NULL },
        { PPP_COMPUDP8, "IP Compression",       NULL,   NULL,   NULL },
        { PPP_COMPRTP8, "IP Compression",       NULL,   NULL,   NULL },
        { PPP_COMPTCPND, "IP Compression",      NULL,   NULL,   NULL },
        { PPP_COMPSTATE, "IP Compression",      NULL,   NULL,   NULL },
        { PPP_COMPUDP16, "IP Compression",      NULL,   NULL,   NULL },
        { PPP_COMPRTP16, "IP Compression",      NULL,   NULL,   NULL },
        { PPP_MPLS,     "MPLS",                 NULL,   NULL,   NULL },
        { PPP_MPLSMC,   "MPLS M/C",             NULL,   NULL,   NULL },
        { PPP_LQR,      "LQR",          interpret_ppp_lqr,      "PPP-LQR:  ",
            "Link Quality Report" },
        { PPP_LCP,      "LCP",          interpret_ppp_cp,       "PPP-LCP:  ",
            "Link Control Protocol" },
        { PPP_IPCP,     "IPCP",         interpret_ppp_cp,       "PPP-IPCP: ",
            "IP Control Protocol" },
        { PPP_IPV6CP,   "IPV6CP",       interpret_ppp_cp,       "PPP-IPV6CP:  ",
            "IPv6 Control Protocol" },
        { PPP_CCP,      "CCP",          interpret_ppp_cp,       "PPP-CCP:  ",
            "Compression Control Protocol" },
        { PPP_CCPFRAG,  "CCP-Link",     interpret_ppp_cp, "PPP-CCP-Link:  ",
            "Per-Link Compression Control Protocol" },
        { PPP_ECP,      "ECP",          interpret_ppp_cp,       "PPP-ECP:  ",
            "Encryption Control Protocol" },
        { PPP_ECPFRAG,  "ECP-Link",     interpret_ppp_cp, "PPP-ECP-Link:  ",
            "Per-Link Encryption Control Protocol" },
        { PPP_MPLSCP,   "MPLSCP",               NULL,   NULL,   NULL },
        { PPP_OSINLCP,  "OSINLCP",              NULL,   NULL,   NULL },
        { PPP_ATCP,     "ATCP",                 NULL,   NULL,   NULL },
        { PPP_IPXCP,    "IPXCP",                NULL,   NULL,   NULL },
        { PPP_BACP,     "BACP",                 NULL,   NULL,   NULL },
        { PPP_BCP,      "BCP",                  NULL,   NULL,   NULL },
        { PPP_CBCP,     "CBCP",                 NULL,   NULL,   NULL },
        { PPP_BAP,      "BAP",                  NULL,   NULL,   NULL },
        { PPP_CHAP,     "CHAP",         interpret_ppp_chap,     "CHAP:  ",
            "Challenge Handshake Authentication Protocl" },
        { PPP_PAP,      "PAP",          interpret_ppp_pap,      "PAP:   ",
            "Password Authentication Protocol" },
        { PPP_EAP,      "EAP",                  NULL,   NULL,   NULL },
        { 0,            unknown_string,         NULL,   NULL,   NULL }
};

static cp_optinfo_t lcp_optinfo[] = {
        { OPT_LCP_VENDOR,       "Vendor-Specific",              6,
            opt_format_vendor },
        { OPT_LCP_MRU,          "Maximum-Receive-Unit",         4,
            opt_format_mru },
        { OPT_LCP_ASYNCMAP,     "Async-Control-Character-Map",  6,
            opt_format_accm },
        { OPT_LCP_AUTHTYPE,     "Authentication-Protocol",      4,
            opt_format_authproto },
        { OPT_LCP_QUALITY,      "Quality-Protocol",             4,
            opt_format_qualproto },
        { OPT_LCP_MAGICNUMBER,  "Magic-Number",                 6,
            opt_format_magicnum },
        { OPT_LCP_PCOMPRESSION, "Protocol-Field-Compression",   2,      NULL },
        { OPT_LCP_ACCOMPRESSION, "Address-and-Control-Field-Compression", 2,
            NULL },
        { OPT_LCP_FCSALTERN,    "FCS-Alternative",              3,
            opt_format_fcs },
        { OPT_LCP_SELFDESCPAD,  "Self-Describing-Padding",      3,
            opt_format_sdp },
        { OPT_LCP_NUMBERED,     "Numbered-Mode",                3,
            opt_format_nummode },
        { OPT_LCP_MULTILINKPROC, "Multi-Link-Procedure",        2,      NULL },
        { OPT_LCP_CALLBACK,     "Callback",                     3,
            opt_format_callback },
        { OPT_LCP_CONNECTTIME,  "Connect-Time",                 2,      NULL },
        { OPT_LCP_COMPOUNDFRAMES, "Compound-Frames",            2,      NULL },
        { OPT_LCP_DATAENCAP,    "Nominal-Data-Encapsulation",   2,      NULL },
        { OPT_LCP_MRRU,         "Multilink-MRRU",               4,
            opt_format_mrru },
        { OPT_LCP_SSNHF,        "Multilink-Short-Sequence-Number-Header-Format",
            2, NULL },
        { OPT_LCP_EPDISC,       "Multilink-Endpoint-Discriminator",     3,
            opt_format_epdisc },
        { OPT_LCP_DCEIDENT,     "DCE-Identifier",               3,
            opt_format_dce },
        { OPT_LCP_MLPLUSPROC,   "Multi-Link-Plus-Procedure",    2,      NULL },
        { OPT_LCP_LINKDISC,     "Link Discriminator for BACP",  4,
            opt_format_linkdisc },
        { OPT_LCP_AUTH,         "LCP-Authentication-Option",    2,      NULL },
        { OPT_LCP_COBS,         "COBS",                         2,      NULL },
        { OPT_LCP_PFXELISION,   "Prefix elision",               2,      NULL },
        { OPT_LCP_MPHDRFMT,     "Multilink header format",      2,      NULL },
        { OPT_LCP_I18N,         "Internationalization",         6,
            opt_format_i18n },
        { OPT_LCP_SDL,          "Simple Data Link on SONET/SDH", 2,     NULL },
        { OPT_LCP_MUXING,       "Old PPP Multiplexing",         2,      NULL },
        { 0,                    unknown_string,                 0,      NULL }
};

static cp_optinfo_t ipcp_optinfo[] = {
        { OPT_IPCP_ADDRS,       "IP-Addresses",                 10,
            opt_format_ipaddresses },
        { OPT_IPCP_COMPRESSTYPE, "IP-Compression-Protocol",     4,
            opt_format_ipcompproto },
        { OPT_IPCP_ADDR,        "IP-Address",                   6,
            opt_format_ipaddress },
        { OPT_IPCP_MOBILEIPV4,  "Mobile-IPv4",                  6,
            opt_format_mobileipv4 },
        { OPT_IPCP_DNS1,        "Primary DNS Address",          6,
            opt_format_ipaddress },
        { OPT_IPCP_NBNS1,       "Primary NBNS Address",         6,
            opt_format_ipaddress },
        { OPT_IPCP_DNS2,        "Secondary DNS Address",        6,
            opt_format_ipaddress },
        { OPT_IPCP_NBNS2,       "Secondary NBNS Address",       6,
            opt_format_ipaddress },
        { OPT_IPCP_SUBNET,      "IP-Subnet",                    6,
            opt_format_ipaddress },
        { 0,                    unknown_string,                 0,      NULL }
};

static cp_optinfo_t ipv6cp_optinfo[] = {
        { OPT_IPV6CP_IFACEID,   "Interface-Identifier",         10,
            opt_format_ifaceid },
        { OPT_IPV6CP_COMPRESSTYPE, "IPv6-Compression-Protocol", 4,
            opt_format_ipv6compproto },
        { 0,                    unknown_string,                 0,      NULL }
};

static cp_optinfo_t ccp_optinfo[] = {
        { OPT_CCP_PROPRIETARY,  "Proprietary Compression OUI",  6,
            opt_format_compoui },
        { OPT_CCP_PREDICTOR1,   "Predictor type 1",             2,      NULL },
        { OPT_CCP_PREDICTOR2,   "Predictor type 2",             2,      NULL },
        { OPT_CCP_PUDDLEJUMP,   "Puddle Jumper",                2,      NULL },
        { OPT_CCP_HPPPC,        "Hewlett-Packard PPC",          2,      NULL },
        { OPT_CCP_STACLZS,      "Stac Electronics LZS",         5,
            opt_format_staclzs },
        { OPT_CCP_MPPC,         "Microsoft PPC",                6,
            opt_format_mppc },
        { OPT_CCP_GANDALFFZA,   "Gandalf FZA",                  3,
            opt_format_gandalf },
        { OPT_CCP_V42BIS,       "V.42bis compression",          2,
            NULL },
        { OPT_CCP_BSDCOMP,      "BSD LZW Compress",             3,
            opt_format_bsdcomp },
        { OPT_CCP_LZSDCP,       "LZS-DCP",                      6,
            opt_format_lzsdcp },
        { OPT_CCP_MAGNALINK,    "Magnalink",                    4,
            opt_format_magnalink },
        { OPT_CCP_DEFLATE,      "Deflate",                      4,
            opt_format_deflate },
        { 0,                    unknown_string,                 0,      NULL }
};

static cp_optinfo_t ecp_optinfo[] = {
        { OPT_ECP_PROPRIETARY,  "Proprietary Encryption OUI",   6,
            opt_format_encroui },
        { OPT_ECP_DESE,         "DESE",                         10,
            opt_format_dese },
        { OPT_ECP_3DESE,        "3DESE",                        10,
            opt_format_dese },
        { OPT_ECP_DESEBIS,      "DESE-bis",                     10,
            opt_format_dese },
        { 0,                    unknown_string,                 0,      NULL }
};

static cp_optinfo_t muxcp_optinfo[] = {
        { OPT_MUXCP_DEFAULTPID, "Default PID",                  4,
            opt_format_muxpid },
        { 0,                    unknown_string,                 0,      NULL }
};

static char *cp_codearray[] = {
        "(Vendor Specific)",
        "(Configure-Request)",
        "(Configure-Ack)",
        "(Configure-Nak)",
        "(Configure-Reject)",
        "(Terminate-Request)",
        "(Terminate-Ack)",
        "(Code-Reject)",
        "(Protocol-Reject)",
        "(Echo-Request)",
        "(Echo-Reply)",
        "(Discard-Request)",
        "(Identification)",
        "(Time-Remaining)",
        "(Reset-Request)",
        "(Reset-Ack)"
};
#define MAX_CPCODE      ((sizeof (cp_codearray) / sizeof (char *)) - 1)

static char *pap_codearray[] = {
        "(Unknown)",
        "(Authenticate-Request)",
        "(Authenticate-Ack)",
        "(Authenticate-Nak)"
};
#define MAX_PAPCODE     ((sizeof (pap_codearray) / sizeof (char *)) - 1)

static char *chap_codearray[] = {
        "(Unknown)",
        "(Challenge)",
        "(Response)",
        "(Success)",
        "(Failure)"
};
#define MAX_CHAPCODE    ((sizeof (chap_codearray) / sizeof (char *)) - 1)


int
interpret_ppp(int flags, uchar_t *data, int len)
{
        uint16_t protocol;
        ppp_protoinfo_t *protoinfo;
        uchar_t *payload = data;

        if (len < 2)
                return (len);

        GETINT16(protocol, payload);
        len -= sizeof (uint16_t);

        protoinfo = ppp_getprotoinfo(protocol);

        if (flags & F_SUM) {
                (void) sprintf(get_sum_line(),
                    "PPP Protocol=0x%x (%s)", protocol, protoinfo->name);
        } else { /* F_DTAIL */
                show_header("PPP:    ", "Point-to-Point Protocol", len);
                show_space();
                (void) sprintf(get_line(0, 0), "Protocol = 0x%x (%s)",
                    protocol, protoinfo->name);
                show_space();
        }

        if (protoinfo->interpret_proto != NULL) {
                len = protoinfo->interpret_proto(flags, payload, len,
                    protoinfo);
        }

        return (len);
}

/*
 * interpret_ppp_cp() - Interpret PPP control protocols.  It is convenient
 * to do some of the decoding of these protocols in a common function since
 * they share packet formats.  This function expects to receive data
 * starting with the code field.
 */
static int
interpret_ppp_cp(int flags, uchar_t *data, int len, ppp_protoinfo_t *protoinfo)
{
        uint8_t code;
        uint8_t id;
        char *codestr;
        uint16_t length;
        uchar_t *datap = data;

        if (len < sizeof (ppp_pkt_t))
                return (len);

        GETINT8(code, datap);
        GETINT8(id, datap);
        GETINT16(length, datap);

        len -= sizeof (ppp_pkt_t);

        if (code <= MAX_CPCODE)
                codestr = cp_codearray[code];
        else
                codestr = "";

        if (flags & F_SUM) {
                (void) sprintf(get_sum_line(),
                    "%s%s", protoinfo->prefix, codestr);
        } else { /* (flags & F_DTAIL) */
                show_header(protoinfo->prefix, protoinfo->description, len);
                show_space();

                (void) sprintf(get_line(0, 0), "Code = %d %s", code, codestr);
                (void) sprintf(get_line(0, 0), "Identifier = %d", id);
                (void) sprintf(get_line(0, 0), "Length = %d", length);

                show_space();

                len = MIN(len, length - sizeof (ppp_pkt_t));
                if (len == 0)
                        return (len);

                switch (code) {
                case CODE_VENDOR: {
                        uint32_t magicnum;
                        uint32_t oui;
                        char *ouistr;
                        uint8_t kind;

                        if (len < sizeof (magicnum) + sizeof (oui))
                                return (len);

                        GETINT32(magicnum, datap);
                        (void) sprintf(get_line(0, 0), "Magic-Number = 0x%08x",
                            magicnum);

                        GETINT32(oui, datap);
                        kind = oui & 0x000000ff;
                        oui >>= 8;

                        ouistr = ether_ouiname(oui);
                        if (ouistr == NULL)
                                ouistr = unknown_string;

                        (void) sprintf(get_line(0, 0), "OUI = 0x%06x (%s)",
                            oui, ouistr);
                        (void) sprintf(get_line(0, 0), "Kind = %d", kind);
                        show_space();
                        break;
                }

                case CODE_CONFREQ:
                case CODE_CONFACK:
                case CODE_CONFNAK:
                case CODE_CONFREJ:
                        /*
                         * The above all contain protocol specific
                         * configuration options.  Parse these options.
                         */
                        interpret_cp_options(datap, len, protoinfo);
                        break;

                case CODE_TERMREQ:
                case CODE_TERMACK:
                        /*
                         * The arbitrary data in these two packet types
                         * is almost always plain text.  Print it as such.
                         */
                        (void) sprintf(get_line(0, 0), "Data = %.*s",
                            length - sizeof (ppp_pkt_t), datap);
                        show_space();
                        break;

                case CODE_CODEREJ:
                        /*
                         * What follows is the rejected control protocol
                         * packet, starting with the code field.
                         * Conveniently, we can call interpret_ppp_cp() to
                         * decode this.
                         */
                        prot_nest_prefix = protoinfo->prefix;
                        interpret_ppp_cp(flags, datap, len, protoinfo);
                        prot_nest_prefix = "";
                        break;

                case CODE_PROTREJ:
                        /*
                         * We don't print the rejected-protocol field
                         * explicitely.  Instead, we cheat and pretend that
                         * the rejected-protocol field is actually the
                         * protocol field in the included PPP packet.  This
                         * way, we can invoke interpret_ppp() and have it
                         * treat the included packet normally.
                         */
                        prot_nest_prefix = protoinfo->prefix;
                        interpret_ppp(flags, datap, len);
                        prot_nest_prefix = "";
                        break;

                case CODE_ECHOREQ:
                case CODE_ECHOREP:
                case CODE_DISCREQ:
                case CODE_IDENT:
                case CODE_TIMEREMAIN: {
                        uint32_t magicnum;
                        char *message_label = "Identification = %.*s";

                        if (len < sizeof (uint32_t))
                                break;

                        GETINT32(magicnum, datap);
                        len -= sizeof (uint32_t);
                        (void) sprintf(get_line(0, 0), "Magic-Number = 0x%08x",
                            magicnum);
                        /*
                         * Unless this is an identification or
                         * time-remaining packet, arbitrary data follows
                         * the magic number field.  The user can take a
                         * look at the hex dump for enlightenment.
                         */
                        if (code == CODE_TIMEREMAIN) {
                                uint32_t timeremaining;

                                if (len < sizeof (uint32_t))
                                        break;

                                message_label = "Message = %.*s";

                                GETINT32(timeremaining, datap);
                                len -= sizeof (uint32_t);
                                (void) sprintf(get_line(0, 0),
                                    "Seconds Remaining = %d", timeremaining);
                        }

                        if (code == CODE_IDENT || code == CODE_TIMEREMAIN) {
                                if (len == 0)
                                        break;

                                (void) sprintf(get_line(0, 0), message_label,
                                    len, datap);
                        }
                        show_space();
                        break;
                }

                /*
                 * Reset-Request and Reset-Ack contain arbitrary data which
                 * the user can sift through using the -x option.
                 */
                case CODE_RESETREQ:
                case CODE_RESETACK:
                default:
                        break;
                }
        }
        return (len);
}


/*
 * interpret_cp_options() decodes control protocol configuration options.
 * Since each control protocol has a different set of options whose type
 * numbers overlap, the protoinfo parameter is used to get a handle on
 * which option set to use for decoding.
 */
static int
interpret_cp_options(uchar_t *optptr, int len, ppp_protoinfo_t *protoinfo)
{
        cp_optinfo_t *optinfo;
        cp_optinfo_t *optinfo_ptr;
        uint8_t optlen;
        uint8_t opttype;

        switch (protoinfo->proto) {
        case PPP_LCP:
                optinfo = lcp_optinfo;
                break;
        case PPP_IPCP:
                optinfo = ipcp_optinfo;
                break;
        case PPP_IPV6CP:
                optinfo = ipv6cp_optinfo;
                break;
        case PPP_CCP:
                optinfo = ccp_optinfo;
                break;
        case PPP_ECP:
                optinfo = ecp_optinfo;
                break;
        case PPP_MUXCP:
                optinfo = muxcp_optinfo;
                break;
        default:
                return (len);
                break;
        }

        if (len >= 2) {
                (void) sprintf(get_line(0, 0), "%s Configuration Options",
                    protoinfo->name);
                show_space();
        }

        while (len >= 2) {
                GETINT8(opttype, optptr);
                GETINT8(optlen, optptr);

                optinfo_ptr = ppp_getoptinfo(optinfo, opttype);

                (void) sprintf(get_line(0, 0), "Option Type = %d (%s)", opttype,
                    optinfo_ptr->opt_name);
                (void) sprintf(get_line(0, 0), "Option Length = %d", optlen);

                /*
                 * Don't continue if there isn't enough data to
                 * contain this option, or if this type of option
                 * should contain more data than the length field
                 * claims there is.
                 */
                if (optlen > len || optlen < optinfo_ptr->opt_minsize) {
                        (void) sprintf(get_line(0, 0),
                            "Warning: Incomplete Option");
                        show_space();
                        break;
                }

                if (optinfo_ptr->opt_formatdata != NULL) {
                        optinfo_ptr->opt_formatdata(optptr,
                            MIN(optlen - 2, len - 2));
                }

                len -= optlen;
                optptr += optlen - 2;

                show_space();
        }

        return (len);
}

static int
interpret_ppp_chap(int flags, uchar_t *data, int len,
    ppp_protoinfo_t *protoinfo)
{
        uint8_t code;
        uint8_t id;
        char *codestr;
        uint16_t length;
        int lengthleft;
        uchar_t *datap = data;


        if (len < sizeof (ppp_pkt_t))
                return (len);

        GETINT8(code, datap);
        GETINT8(id, datap);
        GETINT8(length, datap);

        if (code <= MAX_CHAPCODE)
                codestr = chap_codearray[code];
        else
                codestr = "";

        if (flags & F_SUM) {
                (void) sprintf(get_sum_line(),
                    "%s%s", protoinfo->prefix, codestr);
        } else { /* (flags & F_DTAIL) */
                show_header(protoinfo->prefix, protoinfo->description, len);
                show_space();

                (void) sprintf(get_line(0, 0), "Code = %d %s", code, codestr);
                (void) sprintf(get_line(0, 0), "Identifier = %d", id);
                (void) sprintf(get_line(0, 0), "Length = %d", length);

                show_space();

                if (len < length)
                        return (len);

                lengthleft = len - sizeof (ppp_pkt_t);

                switch (code) {
                case CODE_CHALLENGE:
                case CODE_RESPONSE: {
                        uint8_t value_size;
                        uint16_t peername_size;

                        if (lengthleft < sizeof (value_size))
                                break;

                        GETINT8(value_size, datap);
                        lengthleft -= sizeof (value_size);
                        (void) sprintf(get_line(0, 0), "Value-Size = %d",
                            value_size);

                        if (lengthleft < sizeof (peername_size))
                                break;
                        peername_size = MIN(length - sizeof (ppp_pkt_t) -
                            value_size, lengthleft);
                        (void) sprintf(get_line(0, 0), "Name = %.*s",
                            peername_size, datap + value_size);

                        break;
                }
                case CODE_SUCCESS:
                case CODE_FAILURE: {
                        uint16_t message_size = MIN(length - sizeof (ppp_pkt_t),
                            lengthleft);

                        (void) sprintf(get_line(0, 0), "Message = %.*s",
                            message_size, datap);
                        break;
                }
                default:
                        break;
                }
        }

        show_space();
        len -= length;
        return (len);
}

static int
interpret_ppp_pap(int flags, uchar_t *data, int len,
    ppp_protoinfo_t *protoinfo)
{
        uint8_t code;
        uint8_t id;
        char *codestr;
        uint16_t length;
        int lengthleft;
        uchar_t *datap = data;

        if (len < sizeof (ppp_pkt_t))
                return (len);

        GETINT8(code, datap);
        GETINT8(id, datap);
        GETINT16(length, datap);

        lengthleft = len - sizeof (ppp_pkt_t);

        if (code <= MAX_PAPCODE)
                codestr = pap_codearray[code];
        else
                codestr = "";

        if (flags & F_SUM) {
                (void) sprintf(get_sum_line(),
                    "%s%s", protoinfo->prefix, codestr);
        } else { /* (flags & F_DTAIL) */
                show_header(protoinfo->prefix, protoinfo->description, len);
                show_space();

                (void) sprintf(get_line(0, 0), "Code = %d %s", code, codestr);
                (void) sprintf(get_line(0, 0), "Identifier = %d", id);
                (void) sprintf(get_line(0, 0), "Length = %d", length);

                show_space();

                if (len < length)
                        return (len);

                switch (code) {
                case CODE_AUTHREQ: {
                        uint8_t fieldlen;

                        if (lengthleft < sizeof (fieldlen))
                                break;
                        GETINT8(fieldlen, datap);
                        (void) sprintf(get_line(0, 0), "Peer-Id Length = %d",
                            fieldlen);
                        lengthleft -= sizeof (fieldlen);

                        if (lengthleft < fieldlen)
                                break;
                        (void) sprintf(get_line(0, 0), "Peer-Id = %.*s",
                            fieldlen, datap);
                        lengthleft -= fieldlen;

                        datap += fieldlen;

                        if (lengthleft < sizeof (fieldlen))
                                break;
                        GETINT8(fieldlen, datap);
                        (void) sprintf(get_line(0, 0), "Password Length = %d",
                            fieldlen);
                        lengthleft -= sizeof (fieldlen);

                        if (lengthleft < fieldlen)
                                break;
                        (void) sprintf(get_line(0, 0), "Password = %.*s",
                            fieldlen, datap);

                        break;
                }
                case CODE_AUTHACK:
                case CODE_AUTHNAK: {
                        uint8_t msglen;

                        if (lengthleft < sizeof (msglen))
                                break;
                        GETINT8(msglen, datap);
                        (void) sprintf(get_line(0, 0), "Msg-Length = %d",
                            msglen);
                        lengthleft -= sizeof (msglen);

                        if (lengthleft < msglen)
                                break;
                        (void) sprintf(get_line(0, 0), "Message = %.*s",
                            msglen, datap);

                        break;
                }
                default:
                        break;
                }
        }

        show_space();
        len -= length;
        return (len);
}


static int
interpret_ppp_lqr(int flags, uchar_t *data, int len,
    ppp_protoinfo_t *protoinfo)
{
        lqr_pkt_t lqr_pkt;
        if (len < sizeof (lqr_pkt_t))
                return (len);

        (void) memcpy(&lqr_pkt, data, sizeof (lqr_pkt_t));

        if (flags & F_SUM) {
                (void) sprintf(get_sum_line(), protoinfo->prefix);
        } else { /* (flags & F_DTAIL) */
                show_header(protoinfo->prefix, protoinfo->description, len);
                show_space();

                (void) sprintf(get_line(0, 0), "Magic-Number =   0x%08x",
                    ntohl(lqr_pkt.lqr_magic));
                (void) sprintf(get_line(0, 0), "LastOutLQRs =    %d",
                    ntohl(lqr_pkt.lqr_lastoutlqrs));
                (void) sprintf(get_line(0, 0), "LastOutPackets = %d",
                    ntohl(lqr_pkt.lqr_lastoutpackets));
                (void) sprintf(get_line(0, 0), "LastOutOctets =  %d",
                    ntohl(lqr_pkt.lqr_lastoutoctets));
                (void) sprintf(get_line(0, 0), "PeerInLQRs =     %d",
                    ntohl(lqr_pkt.lqr_peerinlqrs));
                (void) sprintf(get_line(0, 0), "PeerInPackets =  %d",
                    ntohl(lqr_pkt.lqr_peerinpackets));
                (void) sprintf(get_line(0, 0), "PeerInDiscards = %d",
                    ntohl(lqr_pkt.lqr_peerindiscards));
                (void) sprintf(get_line(0, 0), "PeerInErrors =   %d",
                    ntohl(lqr_pkt.lqr_peerinerrors));
                (void) sprintf(get_line(0, 0), "PeerInOctets =   %d",
                    ntohl(lqr_pkt.lqr_peerinoctets));
                (void) sprintf(get_line(0, 0), "PeerOutLQRs =    %d",
                    ntohl(lqr_pkt.lqr_peeroutlqrs));
                (void) sprintf(get_line(0, 0), "PeerOutPackets = %d",
                    ntohl(lqr_pkt.lqr_peeroutpackets));
                (void) sprintf(get_line(0, 0), "PeerOutOctets =  %d",
                    ntohl(lqr_pkt.lqr_peeroutoctets));

                show_space();
        }

        len -= sizeof (lqr_pkt_t);
        return (len);
}

static ppp_protoinfo_t *
ppp_getprotoinfo(uint16_t proto)
{
        ppp_protoinfo_t *protoinfo_ptr = &protoinfo_array[0];

        while (protoinfo_ptr->proto != proto && protoinfo_ptr->proto != 0) {
                protoinfo_ptr++;
        }

        return (protoinfo_ptr);
}


static cp_optinfo_t *
ppp_getoptinfo(cp_optinfo_t optinfo_list[], uint16_t opt_type)
{
        cp_optinfo_t *optinfo_ptr = &optinfo_list[0];

        while (optinfo_ptr->opt_type != opt_type &&
            optinfo_ptr->opt_name != unknown_string) {
                optinfo_ptr++;
        }

        return (optinfo_ptr);
}


/*
 * Below are the functions which parse control protocol configuration
 * options.  The first argument to these functions (optdata) points to the
 * first byte of the option after the length field.  The second argument
 * (size) is the number of bytes in the option after the length field
 * (length - 2).
 */

/*
 * The format of the Vendor-Specific option (rfc2153) is:
 *
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |     Type      |    Length     |              OUI
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *        ...      |     Kind      |  Value(s) ...
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
 */
/*ARGSUSED1*/
static void
opt_format_vendor(uchar_t *optdata, uint8_t size)
{
        uint32_t oui;
        char *ouistr;
        uint8_t kind;

        GETINT32(oui, optdata);
        kind = oui & 0x000000ff;
        oui >>= 8;

        ouistr = ether_ouiname(oui);
        if (ouistr == NULL)
                ouistr = unknown_string;

        (void) sprintf(get_line(0, 0), "OUI = 0x%06x (%s)", oui, ouistr);
        (void) sprintf(get_line(0, 0), "Kind = %d", kind);
}

/*
 * The format of the MRU option (rfc1661) is:
 *
 *  0                   1                   2                   3
 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |     Type      |    Length     |      Maximum-Receive-Unit     |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 */
/*ARGSUSED1*/
static void
opt_format_mru(uchar_t *optdata, uint8_t size)
{
        uint16_t mru;

        GETINT16(mru, optdata);
        (void) sprintf(get_line(0, 0), "MRU = %d", mru);
}

/*
 * The format of the accm option (rfc1662) is:
 *
 *  0                   1                   2                   3
 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |     Type      |    Length     |               ACCM
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *           ACCM (cont)           |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 */
/*ARGSUSED1*/
static void
opt_format_accm(uchar_t *optdata, uint8_t size)
{
        uint32_t accm;

        GETINT32(accm, optdata);
        (void) sprintf(get_line(0, 0), "ACCM = 0x%08x", accm);
}

/*
 * The format of the Authentication-Protocol option (rfc1661) is:
 *
 *  0                   1                   2                   3
 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |     Type      |    Length     |     Authentication-Protocol   |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |    Data ...
 * +-+-+-+-+
 *
 * For PAP (rfc1334), there is no data.  For CHAP (rfc1994), there is one
 * byte of data representing the algorithm.
 */
static void
opt_format_authproto(uchar_t *optdata, uint8_t size)
{
        uint16_t proto;
        ppp_protoinfo_t *auth_protoinfo;

        GETINT16(proto, optdata);

        auth_protoinfo = ppp_getprotoinfo(proto);

        (void) sprintf(get_line(0, 0), "Protocol = 0x%x (%s)", proto,
            auth_protoinfo->name);

        switch (proto) {
        case PPP_CHAP: {
                uint8_t algo;
                char *algostr;

                if (size < sizeof (proto) + sizeof (algo))
                        return;

                GETINT8(algo, optdata);
                switch (algo) {
                case 5:
                        algostr = "CHAP with MD5";
                        break;
                case 128:
                        algostr = "MS-CHAP";
                        break;
                case 129:
                        algostr = "MS-CHAP-2";
                        break;
                default:
                        algostr = unknown_string;
                        break;
                }
                (void) sprintf(get_line(0, 0), "Algorithm = %d (%s)", algo,
                    algostr);
                break;
        }
        default:
                break;
        }
}

/*
 * The format of the Quality Protocol option (rfc1661) is:
 *
 *  0                   1                   2                   3
 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |     Type      |    Length     |        Quality-Protocol       |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |    Data ...
 * +-+-+-+-+
 *
 * For LQR, the data consists of a 4 byte reporting period.
 */
static void
opt_format_qualproto(uchar_t *optdata, uint8_t size)
{
        uint16_t proto;
        ppp_protoinfo_t *qual_protoinfo;

        GETINT16(proto, optdata);

        qual_protoinfo = ppp_getprotoinfo(proto);

        (void) sprintf(get_line(0, 0), "Protocol = 0x%x (%s)", proto,
            qual_protoinfo->name);

        switch (proto) {
        case PPP_LQR: {
                uint32_t reporting_period;

                if (size < sizeof (proto) + sizeof (reporting_period))
                        return;

                GETINT32(reporting_period, optdata);
                (void) sprintf(get_line(0, 0), "Reporting-Period = %d",
                    reporting_period);
                break;
        }
        default:
                break;
        }
}

/*
 * The format of the Magic Number option (rfc1661) is:
 *
 *  0                   1                   2                   3
 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |     Type      |    Length     |          Magic-Number
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *       Magic-Number (cont)       |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 */
/*ARGSUSED1*/
static void
opt_format_magicnum(uchar_t *optdata, uint8_t size)
{
        uint32_t magicnum;

        GETINT32(magicnum, optdata);
        (void) sprintf(get_line(0, 0), "Magic Number = 0x%08x", magicnum);
}

/*
 * The format of the FCS-Alternatives option (rfc1570) is:
 *
 *  0                   1                   2
 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |     Type      |    Length     |    Options    |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 */
/*ARGSUSED1*/
static void
opt_format_fcs(uchar_t *optdata, uint8_t size)
{
        uint8_t options;

        GETINT8(options, optdata);

        (void) sprintf(get_line(0, 0), "Options = 0x%02x", options);
        (void) sprintf(get_line(0, 0), "     %s",
            getflag(options, 0x01, "NULL FCS", ""));
        (void) sprintf(get_line(0, 0), "     %s",
            getflag(options, 0x02, "CCITT 16-bit FCS", ""));
        (void) sprintf(get_line(0, 0), "     %s",
            getflag(options, 0x04, "CCITT 32-bit FCS", ""));
}

/*
 * The format of the Self-Describing-Padding option (rfc1570) is:
 *
 *  0                   1                   2
 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |     Type      |    Length     |    Maximum    |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 */
/*ARGSUSED1*/
static void
opt_format_sdp(uchar_t *optdata, uint8_t size)
{
        uint8_t max;

        GETINT8(max, optdata);

        (void) sprintf(get_line(0, 0), "Maximum = %d", max);
}

/*
 * The format of the Numbered-Mode option (rfc1663) is:
 *
 *  0                   1                   2                   3
 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |     Type      |     Length    |    Window     |   Address...
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 */
/*ARGSUSED1*/
static void
opt_format_nummode(uchar_t *optdata, uint8_t size)
{
        uint8_t window;

        GETINT8(window, optdata);
        (void) sprintf(get_line(0, 0), "Window = %d", window);
}

/*
 * The format of the Callback option (rfc1570) is:
 *
 *  0                   1                   2                   3
 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |     Type      |    Length     |   Operation   |  Message ...
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 */
static void
opt_format_callback(uchar_t *optdata, uint8_t size)
{
        uint8_t operation;
        char *opstr;

        GETINT8(operation, optdata);
        switch (operation) {
        case 0:
                opstr = "User Authentication";
                break;
        case 1:
                opstr = "Dialing String";
                break;
        case 2:
                opstr = "Location Identifier";
                break;
        case 3:
                opstr = "E.164 Number";
                break;
        case 4:
                opstr = "X.500 Distinguished Name";
                break;
        case 6:
                opstr = "CBCP Negotiation";
                break;
        default:
                opstr = unknown_string;
                break;
        }

        (void) sprintf(get_line(0, 0), "Operation = %d (%s)", operation, opstr);

        if (size > sizeof (operation)) {
                (void) sprintf(get_line(0, 0), "Message = %.*s",
                    size - sizeof (operation), optdata);
        }
}

/*
 * The format of the Multilink-MRRU option (rfc1990) is:
 *
 *  0                   1                   2                   3
 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |   Type = 17   |   Length = 4  | Max-Receive-Reconstructed-Unit|
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 */
/*ARGSUSED1*/
static void
opt_format_mrru(uchar_t *optdata, uint8_t size)
{
        uint16_t mrru;

        GETINT16(mrru, optdata);
        (void) sprintf(get_line(0, 0), "MRRU = %d", mrru);
}

/*
 * The format of the Endpoint Discriminator option (rfc1990) is:
 *
 *  0                   1                   2                   3
 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |   Type = 19   |     Length    |    Class      |  Address ...
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 */
static void
opt_format_epdisc(uchar_t *optdata, uint8_t size)
{
        uint8_t class;
        char *classstr;
        uint8_t addrlen = size - sizeof (class);
        char *addr;

        GETINT8(class, optdata);

        switch (class) {
        case 0:
                classstr = "Null Class";
                break;
        case 1:
                classstr = "Locally Assigned Address";
                break;
        case 2:
                classstr = "IPv4 Address";
                break;
        case 3:
                classstr = "IEE 802.1 Global MAC Address";
                break;
        case 4:
                classstr = "PPP Magic-Number Block";
                break;
        case 5:
                classstr = "Public Switched Network Directory Number";
                break;
        default:
                classstr = unknown_string;
                break;
        }

        (void) sprintf(get_line(0, 0), "Address Class = %d (%s)", class,
            classstr);

        if (addrlen == 0)
                return;

        addr = (char *)malloc(addrlen);
        (void) memcpy(addr, optdata, addrlen);
        switch (class) {
        case 2: {
                char addrstr[INET_ADDRSTRLEN];

                if (addrlen != sizeof (in_addr_t))
                        break;
                if (inet_ntop(AF_INET, addr, addrstr, INET_ADDRSTRLEN) !=
                    NULL) {
                        (void) sprintf(get_line(0, 0), "Address = %s", addrstr);
                }
                break;
        }
        case 3: {
                char *addrstr;

                if (addrlen != sizeof (struct ether_addr))
                        break;
                if ((addrstr = ether_ntoa((struct ether_addr *)addr)) != NULL) {
                        (void) sprintf(get_line(0, 0), "Address = %s", addrstr);
                }
                break;
        }
        case 5: {
                /*
                 * For this case, the address is supposed to be a plain
                 * text telephone number.
                 */
                (void) sprintf(get_line(0, 0), "Address = %.*s", addrlen,
                    addr);
        }
        default:
                break;
        }

        free(addr);
}

/*
 * The DCE identifier option has the following format (from rfc1976):
 *
 *     0                   1                   2
 *     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
 *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *    |     Type      |    Length     |      Mode     |
 *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 */
/*ARGSUSED1*/
static void
opt_format_dce(uchar_t *optdata, uint8_t size)
{
        uint8_t mode;
        char *modestr;

        GETINT8(mode, optdata);
        switch (mode) {
        case 1:
                modestr = "No Additional Negotiation";
                break;
        case 2:
                modestr = "Full PPP Negotiation and State Machine";
                break;
        default:
                modestr = unknown_string;
                break;
        }
        (void) sprintf(get_line(0, 0), "Mode = %d (%s)", mode, modestr);
}

/*
 * The format of the Link Discriminator option (rfc2125) is:
 *
 *  0                   1                   2                   3
 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |     Type      |     Length    |       Link Discriminator      |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 */
/*ARGSUSED1*/
static void
opt_format_linkdisc(uchar_t *optdata, uint8_t size)
{
        uint16_t discrim;

        GETINT16(discrim, optdata);

        (void) sprintf(get_line(0, 0), "Link Discriminator = %d", discrim);
}


/*
 * The format of the Internationalization option (rfc2484) is:
 *
 *  0                   1                   2                   3
 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |     Type      |    Length     |          MIBenum
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *           MIBenum (cont)        |        Language-Tag...
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 */
static void
opt_format_i18n(uchar_t *optdata, uint8_t size)
{
        uint32_t mibenum;
        uint8_t taglen;

        taglen = size - sizeof (mibenum);

        GETINT32(mibenum, optdata);
        (void) sprintf(get_line(0, 0), "MIBenum = %d", mibenum);

        if (taglen > 0) {
                (void) sprintf(get_line(0, 0), "Language Tag = %.*s", taglen,
                    optdata);
        }
}

/*
 * The format of the obsolete IP-Addresses option (rfc1172) is:
 *
 *  0                   1                   2                   3
 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |     Type      |    Length     |     Source-IP-Address
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *   Source-IP-Address (cont)      |  Destination-IP-Address
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *  Destination-IP-Address (cont)  |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 */
/*ARGSUSED1*/
static void
opt_format_ipaddresses(uchar_t *optdata, uint8_t size)
{
        in_addr_t addr;
        char addrstr[INET_ADDRSTRLEN];

        (void) memcpy(&addr, optdata, sizeof (in_addr_t));
        if (inet_ntop(AF_INET, &addr, addrstr, INET_ADDRSTRLEN) != NULL) {
                (void) sprintf(get_line(0, 0), "Source Address =      %s",
                    addrstr);
        }

        optdata += sizeof (in_addr_t);

        (void) memcpy(&addr, optdata, sizeof (in_addr_t));
        if (inet_ntop(AF_INET, &addr, addrstr, INET_ADDRSTRLEN) != NULL) {
                (void) sprintf(get_line(0, 0), "Destination Address = %s",
                    addrstr);
        }
}

/*
 * The format of the IP-Compression-Protocol option (rfc1332) is:
 *
 *  0                   1                   2                   3
 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |     Type      |    Length     |     IP-Compression-Protocol   |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |    Data ...
 * +-+-+-+-+
 *
 * For VJ Compressed TCP/IP, data consists of:
 *
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |  Max-Slot-Id  | Comp-Slot-Id  |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *
 * For IPHC (rfc2509), data consists of:
 *
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |           TCP_SPACE           |         NON_TCP_SPACE         |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |         F_MAX_PERIOD          |          F_MAX_TIME           |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |           MAX_HEADER          |          suboptions...
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 */
static void
opt_format_ipcompproto(uchar_t *optdata, uint8_t size)
{
        uint16_t proto;
        ppp_protoinfo_t *comp_protoinfo;

        GETINT16(proto, optdata);

        comp_protoinfo = ppp_getprotoinfo(proto);

        (void) sprintf(get_line(0, 0), "Protocol = 0x%x (%s)", proto,
            comp_protoinfo->name);

        switch (proto) {
        case PPP_VJC_COMP: {
                uint8_t maxslotid;
                uint8_t compslotid;

                if (size < sizeof (proto) + sizeof (maxslotid) +
                    sizeof (compslotid))
                        break;

                GETINT8(maxslotid, optdata);
                GETINT8(compslotid, optdata);
                (void) sprintf(get_line(0, 0), "Max-Slot-Id = %d", maxslotid);
                (void) sprintf(get_line(0, 0), "Comp-Slot Flag = 0x%x",
                    compslotid);
                break;
        }
        case PPP_FULLHDR: {
                uint16_t tcp_space;
                uint16_t non_tcp_space;
                uint16_t f_max_period;
                uint16_t f_max_time;
                uint16_t max_header;

                if (size < sizeof (proto) + sizeof (tcp_space) +
                    sizeof (non_tcp_space) + sizeof (f_max_period) +
                    sizeof (f_max_time) + sizeof (max_header))
                        break;

                GETINT16(tcp_space, optdata);
                GETINT16(non_tcp_space, optdata);
                GETINT16(f_max_period, optdata);
                GETINT16(f_max_time, optdata);
                GETINT16(max_header, optdata);

                (void) sprintf(get_line(0, 0), "TCP_SPACE = %d", tcp_space);
                (void) sprintf(get_line(0, 0), "NON_TCP_SPACE = %d",
                    non_tcp_space);
                (void) sprintf(get_line(0, 0), "F_MAX_PERIOD = %d",
                    f_max_period);
                (void) sprintf(get_line(0, 0), "F_MAX_TIME = %d", f_max_time);
                (void) sprintf(get_line(0, 0), "MAX_HEADER = %d octets",
                    max_header);
        }
        default:
                break;
        }
}

/*
 * The format of the IP-Address option (rfc1332) is:
 *
 *  0                   1                   2                   3
 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |     Type      |    Length     |           IP-Address
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *         IP-Address (cont)       |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 */
/*ARGSUSED1*/
static void
opt_format_ipaddress(uchar_t *optdata, uint8_t size)
{
        in_addr_t ipaddr;
        char addrstr[INET_ADDRSTRLEN];

        (void) memcpy(&ipaddr, optdata, sizeof (in_addr_t));
        if (inet_ntop(AF_INET, &ipaddr, addrstr, INET_ADDRSTRLEN) != NULL) {
                (void) sprintf(get_line(0, 0), "Address = %s", addrstr);
        }
}

/*
 * The format of the Mobile-IPv4 option (rfc2290) is:
 *
 *  0                   1                   2                   3
 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |     Type      |    Length     |         Mobile Node's ...
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *       ...  Home Address         |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 */
/*ARGSUSED1*/
static void
opt_format_mobileipv4(uchar_t *optdata, uint8_t size)
{
        in_addr_t ipaddr;
        char addrstr[INET_ADDRSTRLEN];

        (void) memcpy(&ipaddr, optdata, sizeof (in_addr_t));
        if (inet_ntop(AF_INET, &ipaddr, addrstr, INET_ADDRSTRLEN) != NULL) {
                (void) sprintf(get_line(0, 0),
                    "Mobile Node's Home Address = %s", addrstr);
        }
}

/*
 * The format of the Interface-Identifier option (rfc2472) is:
 *
 * 0                   1                   2                   3
 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |     Type      |    Length     | Interface-Identifier (MS Bytes)
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *                      Interface-Identifier (cont)
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * Interface-Identifier (LS Bytes) |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 */
/*ARGSUSED1*/
static void
opt_format_ifaceid(uchar_t *optdata, uint8_t size)
{
        in6_addr_t id;
        char idstr[INET6_ADDRSTRLEN];

        (void) memset(&id, 0, sizeof (in6_addr_t));
        (void) memcpy(&id.s6_addr[8], optdata, 8);

        if (inet_ntop(AF_INET6, &id, idstr, INET6_ADDRSTRLEN) != NULL) {
                (void) sprintf(get_line(0, 0), "Interface ID = %s", idstr);
        }
}

/*
 * The format of the IPv6-Compression-Protocol option (rfc2472) is:
 *
 * 0                   1                   2                   3
 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |     Type      |    Length     |   IPv6-Compression-Protocol   |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |    Data ...
 * +-+-+-+-+
 */
static void
opt_format_ipv6compproto(uchar_t *optdata, uint8_t size)
{
        uint16_t proto;
        ppp_protoinfo_t *comp_protoinfo;

        GETINT16(proto, optdata);

        comp_protoinfo = ppp_getprotoinfo(proto);

        (void) sprintf(get_line(0, 0), "Protocol = 0x%x (%s)", proto,
            comp_protoinfo->name);

        switch (proto) {
        case PPP_FULLHDR: {
                uint16_t tcp_space;
                uint16_t non_tcp_space;
                uint16_t f_max_period;
                uint16_t f_max_time;
                uint16_t max_header;

                if (size < sizeof (proto) + sizeof (tcp_space) +
                    sizeof (non_tcp_space) + sizeof (f_max_period) +
                    sizeof (f_max_time) + sizeof (max_header))
                        return;

                GETINT16(tcp_space, optdata);
                GETINT16(non_tcp_space, optdata);
                GETINT16(f_max_period, optdata);
                GETINT16(f_max_time, optdata);
                GETINT16(max_header, optdata);

                (void) sprintf(get_line(0, 0), "TCP_SPACE = %d", tcp_space);
                (void) sprintf(get_line(0, 0), "NON_TCP_SPACE = %d",
                    non_tcp_space);
                (void) sprintf(get_line(0, 0), "F_MAX_PERIOD = %d",
                    f_max_period);
                (void) sprintf(get_line(0, 0), "F_MAX_TIME = %d", f_max_time);
                (void) sprintf(get_line(0, 0), "MAX_HEADER = %d octets",
                    max_header);
        }
        default:
                break;
        }
}

/*
 * The format of the Proprietary Compression OUI option (rfc1962) is:
 *
 *  0                   1                   2                   3
 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |     Type      |    Length     |       OUI ...
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *       OUI       |    Subtype    |  Values...
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
 */
/*ARGSUSED1*/
static void
opt_format_compoui(uchar_t *optdata, uint8_t size)
{
        uint32_t oui;
        uint8_t subtype;
        char *ouistr;

        GETINT32(oui, optdata);
        subtype = oui & 0x000000ff;
        oui >>= 8;

        ouistr = ether_ouiname(oui);
        if (ouistr == NULL)
                ouistr = unknown_string;
        (void) sprintf(get_line(0, 0), "OUI = 0x%06x (%s)", oui, ouistr);
        (void) sprintf(get_line(0, 0), "Subtype = 0x%x", subtype);
}

/*
 * The format of the Stac LZS configuration option (rfc1974) is:
 *
 *  0                   1                   2                   3
 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |     Type      |    Length     |        History Count          |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |   Check Mode  |
 * +-+-+-+-+-+-+-+-+
 */
/*ARGSUSED1*/
static void
opt_format_staclzs(uchar_t *optdata, uint8_t size)
{
        uint16_t hcount;
        uint8_t cmode;

        GETINT16(hcount, optdata);
        GETINT8(cmode, optdata);

        cmode &= 0x07;

        (void) sprintf(get_line(0, 0), "History Count = %d", hcount);
        (void) sprintf(get_line(0, 0), "Check Mode = %d", cmode);
}

/*
 * The format of MPPC configuration option (rfc2118) is:
 *
 *  0                   1                   2                   3
 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |     Type      |    Length     |        Supported Bits         |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |       Supported Bits          |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 */
/*ARGSUSED1*/
static void
opt_format_mppc(uchar_t *optdata, uint8_t size)
{
        uint32_t sb;

        GETINT32(sb, optdata);

        (void) sprintf(get_line(0, 0), "Supported Bits = 0x%x", sb);
}

/*
 * The format of the Gandalf FZA configuration option (rfc1993) is:
 *
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |     Type      |    Length     |   History   |    Version ...
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 */
/*ARGSUSED1*/
static void
opt_format_gandalf(uchar_t *optdata, uint8_t size)
{
        uint8_t history;

        GETINT8(history, optdata);
        (void) sprintf(get_line(0, 0), "Maximum History Size = %d bits",
            history);
}

/*
 * The format of the BSD Compress configuration option (rfc1977) is:
 *
 *  0                   1                   2
 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |     Type      |    Length     | Vers|   Dict  |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 */
/*ARGSUSED1*/
static void
opt_format_bsdcomp(uchar_t *optdata, uint8_t size)
{
        uint8_t version;
        uint8_t codesize;

        GETINT8(codesize, optdata);

        version = codesize >> 5;
        codesize &= 0x1f;

        (void) sprintf(get_line(0, 0), "Version = 0x%x", version);
        (void) sprintf(get_line(0, 0), "Maximum Code Size = %d bits", codesize);
}

/*
 * The format of the LZS-DCP configuration option (rfc1967) is:
 *
 *  0                   1                   2                   3
 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |     Type      |    Length     |        History Count          |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |   Check Mode  | Process Mode  |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 */
/*ARGSUSED1*/
static void
opt_format_lzsdcp(uchar_t *optdata, uint8_t size)
{
        uint16_t history;
        uint8_t mode;
        char *modestr;

        GETINT16(history, optdata);
        (void) sprintf(get_line(0, 0), "History Count = %d", history);

        /* check mode */
        GETINT8(mode, optdata);
        switch (mode) {
        case 0:
                modestr = "None";
                break;
        case 1:
                modestr = "LCB";
                break;
        case 2:
                modestr = "Sequence Number";
                break;
        case 3:
                modestr = "Sequence Number + LCB (default)";
                break;
        default:
                modestr = unknown_string;
                break;
        }
        (void) sprintf(get_line(0, 0), "Check Mode = %d (%s)", mode, modestr);

        /* process mode */
        GETINT8(mode, optdata);
        switch (mode) {
        case 0:
                modestr = "None (default)";
                break;
        case 1:
                modestr = "Process-Uncompressed";
                break;
        default:
                modestr = unknown_string;
                break;
        }
        (void) sprintf(get_line(0, 0), "Process Mode = %d (%s)", mode, modestr);

}

/*
 * The format of the Magnalink configuration option (rfc1975) is:
 *
 *  0                   1                   2                   3
 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |     Type      |    Length     |FE |P| History |  # Contexts   |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 */
/*ARGSUSED1*/
static void
opt_format_magnalink(uchar_t *optdata, uint8_t size)
{
        uint8_t features;
        uint8_t pflag;
        uint8_t history;
        uint8_t contexts;

        GETINT8(history, optdata);
        GETINT8(contexts, optdata);

        features = history >> 6;
        pflag = (history >> 5) & 0x01;
        history &= 0x1f;

        (void) sprintf(get_line(0, 0), "Features = 0x%d", features);
        (void) sprintf(get_line(0, 0), "Packet Flag = %d", pflag);
        (void) sprintf(get_line(0, 0), "History Size = %d", history);
        (void) sprintf(get_line(0, 0), "Contexts = %d", contexts);
}

/*
 * The format of the Deflate configuration option (rfc1979) is:
 *
 *  0                   1                   2                   3
 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |     Type      |    Length     |Window | Method|    MBZ    |Chk|
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 */
/*ARGSUSED1*/
static void
opt_format_deflate(uchar_t *optdata, uint8_t size)
{
        uint8_t window;
        uint8_t method;
        uint8_t chk;

        GETINT8(method, optdata);
        window = method >> 4;
        method &= 0x0f;

        GETINT8(chk, optdata);
        chk &= 0x03;

        (void) sprintf(get_line(0, 0), "Maximum Window Size = %d", window);
        (void) sprintf(get_line(0, 0), "Compression Method = 0x%x", method);
        (void) sprintf(get_line(0, 0), "Check Method = 0x%x", chk);
}

/*
 * The format of the Proprietary Encryption OUI option (rfc1968) is:
 *
 * 0                   1                   2                   3
 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |     Type      |    Length     |       OUI ...
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *       OUI       |    Subtype    |  Values...
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
 */
/*ARGSUSED1*/
static void
opt_format_encroui(uchar_t *optdata, uint8_t size)
{
        uint32_t oui;
        uint8_t subtype;
        char *ouistr;

        GETINT32(oui, optdata);
        subtype = oui & 0x000000ff;
        oui >>= 8;

        ouistr = ether_ouiname(oui);
        if (ouistr == NULL)
                ouistr = unknown_string;
        (void) sprintf(get_line(0, 0), "OUI = 0x%06x (%s)", oui, ouistr);
        (void) sprintf(get_line(0, 0), "Subtype = 0x%x", subtype);
}

/*
 * The format of the DESE, DESE-bis, and 3DESE configuration options
 * (rfc1969, rfc2419, and rfc2420) are:
 *
 * 0                   1                   2                   3
 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |   Type = 3    |    Length     |         Initial Nonce ...
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 */
/*ARGSUSED1*/
static void
opt_format_dese(uchar_t *optdata, uint8_t size)
{
        (void) sprintf(get_line(0, 0),
            "Initial Nonce = 0x%02x%02x%02x%02x%02x%02x%02x%02x",
            optdata[0], optdata[1], optdata[2], optdata[3], optdata[4],
            optdata[5], optdata[6], optdata[7]);
}

/*
 * The format of the PPPMux Default Protocol Id option
 * (draft-ietf-pppext-pppmux-02.txt) is:
 *
 *  0                   1                   2                   3
 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |   Type = 1    |   Length = 4  |        Default PID            |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 */
/*ARGSUSED1*/
static void
opt_format_muxpid(uchar_t *optdata, uint8_t size)
{
        uint16_t defpid;

        GETINT16(defpid, optdata);
        (void) sprintf(get_line(0, 0), "Default PID = %d", defpid);
}