root/usr/src/cmd/mdb/common/modules/arp/arp.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (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 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <stdio.h>
#include <sys/types.h>
#include <sys/stropts.h>
#include <sys/stream.h>
#include <sys/dlpi.h>
#include <sys/hook.h>
#include <sys/hook_event.h>
#include <inet/led.h>
#include <inet/common.h>
#include <inet/mi.h>
#include <inet/arp.h>
#include <inet/ip.h>
#include <netinet/arp.h>

#include <mdb/mdb_modapi.h>
#include <mdb/mdb_ks.h>

typedef struct {
        uint32_t        act_cmd;
        char            *act_name;
        char            *act_type;
} arp_cmd_tbl;

/*
 * removed all the ace/arl related stuff. The only thing that remains
 * is code for dealing with ioctls and printing out arp header that
 * should probably be moved into the ip/mdb module.
 */

/*
 * Print an ARP hardware and protocol address pair; used when printing an ARP
 * message.
 */
static void
print_arp(char field_id, const uchar_t *buf, const arh_t *arh, uint16_t ptype)
{
        char macstr[ARP_MAX_ADDR_LEN*3];
        in_addr_t inaddr;

        if (arh->arh_hlen == 0)
                (void) strcpy(macstr, "(none)");
        else
                mdb_mac_addr(buf, arh->arh_hlen, macstr, sizeof (macstr));
        mdb_printf("%?s  ar$%cha %s\n", "", field_id, macstr);
        if (arh->arh_plen == 0) {
                mdb_printf("%?s  ar$%cpa (none)\n", "", field_id);
        } else if (ptype == IP_ARP_PROTO_TYPE) {
                mdb_printf("%?s  ar$%cpa (unknown)\n", "", field_id);
        } else if (arh->arh_plen == sizeof (in_addr_t)) {
                (void) memcpy(&inaddr, buf + arh->arh_hlen, sizeof (inaddr));
                mdb_printf("%?s  ar$%cpa %I\n", "", field_id, inaddr);
        } else {
                mdb_printf("%?s  ar$%cpa (malformed IP)\n", "", field_id);
        }
}

/*
 * Decode an ARP message and display it.
 */
/* ARGSUSED2 */
static int
arphdr_cmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
        struct {
                arh_t arh;
                uchar_t addrs[4 * ARP_MAX_ADDR_LEN];
        } arp;
        size_t blen;
        uint16_t htype, ptype, op;
        const char *cp;

        if (!(flags & DCMD_ADDRSPEC)) {
                mdb_warn("address required to print ARP header\n");
                return (DCMD_ERR);
        }

        if (mdb_vread(&arp.arh, sizeof (arp.arh), addr) == -1) {
                mdb_warn("unable to read ARP header at %p", addr);
                return (DCMD_ERR);
        }
        mdb_nhconvert(&htype, arp.arh.arh_hardware, sizeof (htype));
        mdb_nhconvert(&ptype, arp.arh.arh_proto, sizeof (ptype));
        mdb_nhconvert(&op, arp.arh.arh_operation, sizeof (op));

        switch (htype) {
        case ARPHRD_ETHER:
                cp = "Ether";
                break;
        case ARPHRD_IEEE802:
                cp = "IEEE802";
                break;
        case ARPHRD_IB:
                cp = "InfiniBand";
                break;
        default:
                cp = "Unknown";
                break;
        }
        mdb_printf("%?p: ar$hrd %x (%s)\n", addr, htype, cp);
        mdb_printf("%?s  ar$pro %x (%s)\n", "", ptype,
            ptype == IP_ARP_PROTO_TYPE ? "IP" : "Unknown");

        switch (op) {
        case ARPOP_REQUEST:
                cp = "ares_op$REQUEST";
                break;
        case ARPOP_REPLY:
                cp = "ares_op$REPLY";
                break;
        case REVARP_REQUEST:
                cp = "arev_op$REQUEST";
                break;
        case REVARP_REPLY:
                cp = "arev_op$REPLY";
                break;
        default:
                cp = "Unknown";
                break;
        }
        mdb_printf("%?s  ar$op %d (%s)\n", "", op, cp);

        /*
         * Note that we go to some length to attempt to print out the fixed
         * header data before trying to decode the variable-length data.  This
         * is done to maximize the amount of useful information shown when the
         * buffer is truncated or otherwise corrupt.
         */
        blen = 2 * (arp.arh.arh_hlen + arp.arh.arh_plen);
        if (mdb_vread(&arp.addrs, blen, addr + sizeof (arp.arh)) == -1) {
                mdb_warn("unable to read ARP body at %p", addr);
                return (DCMD_ERR);
        }

        print_arp('s', arp.addrs, &arp.arh, ptype);
        print_arp('t', arp.addrs + arp.arh.arh_hlen + arp.arh.arh_plen,
            &arp.arh, ptype);
        return (DCMD_OK);
}

static const mdb_dcmd_t dcmds[] = {
        { "arphdr", ":", "display an ARP header", arphdr_cmd, NULL },
        { NULL }
};

/* Note: ar_t walker is in genunix.c and net.c; generic MI walker */
static const mdb_walker_t walkers[] = {
        { NULL }
};

static const mdb_modinfo_t modinfo = { MDB_API_VERSION, dcmds, walkers };

const mdb_modinfo_t *
_mdb_init(void)
{
        return (&modinfo);
}

void
_mdb_fini(void)
{
}