root/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rip.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 2006 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define RIPVERSION      RIPv2
#include <protocols/routed.h>
#include "snoop.h"

static const char *show_cmd(int);
static int get_numtokens(unsigned int);
static const struct rip_sec_entry *rip_next_sec_entry(
    const struct rip_sec_entry *, int);

int
interpret_rip(int flags, struct rip *rip, int fraglen)
{
        const struct netinfo *nip;
        const struct entryinfo *ep;
        const struct netauth *nap;
        const struct rip_sec_entry *rsep, *rsn;
        const struct rip_emetric *rep;
        const uint32_t *tokp;
        int len, count;
        const char *cmdstr, *auth;
        struct in_addr dst;
        uint32_t mval;
        const struct sockaddr_in *sin;
        /* Room for IP destination + "/" + IP mask */
        char addrstr[15+1+15+1];
        /* Room for "RIPv" + uint8_t as %d */
        char ripvers[4+3+1];

        /* RIP header is 4 octets long */
        if ((len = fraglen - 4) < 0)
                return (0);

        if (flags & F_SUM) {
                switch (rip->rip_cmd) {
                case RIPCMD_REQUEST:    cmdstr = "C";           break;
                case RIPCMD_RESPONSE:   cmdstr = "R";           break;
                case RIPCMD_TRACEON:    cmdstr = "Traceon";     break;
                case RIPCMD_TRACEOFF:   cmdstr = "Traceoff";    break;
                case RIPCMD_POLL:       cmdstr = "Poll";        break;
                case RIPCMD_POLLENTRY:  cmdstr = "Poll entry";  break;
                case RIPCMD_SEC_RESPONSE: cmdstr = "R - SEC";   break;
                case RIPCMD_SEC_T_RESPONSE: cmdstr = "R - SEC_T"; break;
                default: cmdstr = "?"; break;
                }

                if (rip->rip_vers == RIPv1)
                        (void) strlcpy(ripvers, "RIP", sizeof (ripvers));
                else
                        (void) snprintf(ripvers, sizeof (ripvers), "RIPv%d",
                            rip->rip_vers);

                switch (rip->rip_cmd) {
                case RIPCMD_REQUEST:
                case RIPCMD_RESPONSE:
                case RIPCMD_POLL:
                        nip = rip->rip_nets;
                        auth = "";
                        if (len >= sizeof (*nip) &&
                            nip->n_family == RIP_AF_AUTH) {
                                nap = (struct netauth *)nip;
                                len -= sizeof (*nip);
                                if (nap->a_type == RIP_AUTH_MD5 &&
                                    len >= ntohs(nap->au.a_md5.md5_auth_len))
                                        len -= ntohs(nap->au.a_md5.
                                            md5_auth_len);
                                auth = " +Auth";
                        }
                        count = len / sizeof (*nip);
                        len %= sizeof (*nip);
                        (void) snprintf(get_sum_line(), MAXLINE,
                            "%s %s (%d destinations%s%s)", ripvers, cmdstr,
                            count, (len != 0 ? "?" : ""), auth);
                        break;

                case RIPCMD_TRACEON:
                case RIPCMD_TRACEOFF:
                        (void) snprintf(get_sum_line(), MAXLINE,
                            "%s %s File=\"%.*s\"", ripvers, cmdstr, len,
                            rip->rip_tracefile);
                        len = 0;
                        break;

                case RIPCMD_SEC_RESPONSE:
                case RIPCMD_SEC_T_RESPONSE:
                        if (len < sizeof (rip->rip_tsol.rip_generation))
                                break;
                        len -= sizeof (rip->rip_tsol.rip_generation);
                        count = 0;
                        rsep = rip->rip_tsol.rip_sec_entry;
                        while (len > 0) {
                                rsn = rip_next_sec_entry(rsep, len);
                                if (rsn == NULL)
                                        break;
                                len -= (const char *)rsn - (const char *)rsep;
                                rsep = rsn;
                                count++;
                        }
                        (void) snprintf(get_sum_line(), MAXLINE,
                            "%s %s (%d destinations%s)", ripvers, cmdstr,
                            count, (len != 0 ? "?" : ""));
                        break;

                default:
                        (void) snprintf(get_sum_line(), MAXLINE,
                            "%s %d (%s)", ripvers, rip->rip_cmd, cmdstr);
                        len = 0;
                        break;
                }
        }

        if (flags & F_DTAIL) {

                len = fraglen - 4;
                show_header("RIP:  ", "Routing Information Protocol", fraglen);
                show_space();
                (void) snprintf(get_line(0, 0), get_line_remain(),
                    "Opcode = %d (%s)", rip->rip_cmd,
                    show_cmd(rip->rip_cmd));
                (void) snprintf(get_line(0, 0), get_line_remain(),
                    "Version = %d", rip->rip_vers);

                switch (rip->rip_cmd) {
                case RIPCMD_REQUEST:
                case RIPCMD_RESPONSE:
                case RIPCMD_POLL:
                        show_space();
                        (void) snprintf(get_line(0, 0), get_line_remain(),
                            "Destination                     Next Hop        "
                            "Tag    Metric");
                        for (nip = rip->rip_nets; len >= sizeof (*nip); nip++,
                            len -= sizeof (*nip)) {
                                if (nip->n_family == RIP_AF_AUTH) {
                                        nap = (const struct netauth *)nip;
                                        if (nap->a_type == RIP_AUTH_NONE) {
                                                (void) snprintf(get_line
                                                    ((char *)nip - dlc_header,
                                                        sizeof (*nip)),
                                                    get_line_remain(),
                                                    " *** Auth None");
                                        } else if (nap->a_type == RIP_AUTH_PW) {
                                                (void) snprintf(get_line
                                                    ((char *)nip - dlc_header,
                                                        sizeof (*nip)),
                                                    get_line_remain(),
                                                    " *** Auth PW \"%.*s\"",
                                                    RIP_AUTH_PW_LEN,
                                                    nap->au.au_pw);
                                        } else if (nap->a_type ==
                                            RIP_AUTH_MD5) {
                                                (void) snprintf(get_line(0, 0),
                                                    get_line_remain(),
                                                    " *** Auth MD5 pkt len %d, "
                                                    "keyid %d, sequence %08lX, "
                                                    "authlen %d",
                                                    ntohs(nap->au.a_md5.
                                                        md5_pkt_len),
                                                    nap->au.a_md5.md5_keyid,
                                                    (long)ntohl(nap->au.a_md5.
                                                        md5_seqno),
                                                    ntohs(nap->au.a_md5.
                                                        md5_auth_len));
                                                if (len - sizeof (*nip) >=
                                                    ntohs(nap->au.a_md5.
                                                    md5_auth_len))
                                                        len -= ntohs(nap->au.
                                                            a_md5.md5_auth_len);
                                                else
                                                        len = sizeof (*nip);
                                        } else {
                                                (void) snprintf(get_line
                                                    ((char *)nip - dlc_header,
                                                        sizeof (*nip)),
                                                    get_line_remain(),
                                                    " *** Auth Type %d?",
                                                    ntohs(nap->a_type));
                                        }
                                        continue;
                                }
                                if (nip->n_family == RIP_AF_UNSPEC &&
                                    rip->rip_cmd == RIPCMD_REQUEST) {
                                        (void) snprintf(get_line(0, 0),
                                            get_line_remain(),
                                            " *** All routes");
                                        continue;
                                }
                                if (nip->n_family != RIP_AF_INET) {
                                        (void) snprintf(get_line(0, 0),
                                            get_line_remain(),
                                            " *** Address Family %d?",
                                            ntohs(nip->n_family));
                                        continue;
                                }
                                if (nip->n_dst == htonl(RIP_DEFAULT)) {
                                        (void) strcpy(addrstr, "default");
                                } else {
                                        dst.s_addr = nip->n_dst;
                                        (void) strlcpy(addrstr, inet_ntoa(dst),
                                            sizeof (addrstr));
                                }
                                if (nip->n_dst != htonl(RIP_DEFAULT) &&
                                    rip->rip_vers >= RIPv2) {
                                        count = strlen(addrstr);
                                        mval = ntohl(nip->n_mask);
                                        /* LINTED */
                                        if (mval == INADDR_ANY) {
                                                /* No mask */;
                                        } else if ((mval + (mval & -mval)) ==
                                            0) {
                                                (void) snprintf(addrstr + count,
                                                    sizeof (addrstr) - count,
                                                    "/%d", 33 - ffs(mval));
                                        } else {
                                                dst.s_addr = nip->n_mask;
                                                (void) snprintf(addrstr + count,
                                                    sizeof (addrstr) - count,
                                                    "/%s", inet_ntoa(dst));
                                        }
                                }
                                dst.s_addr = nip->n_nhop;
                                mval = ntohl(nip->n_metric);
                                (void) snprintf(get_line(0, 0),
                                    get_line_remain(),
                                    "%-31s %-15s %-6d %d%s",
                                    addrstr,
                                    dst.s_addr == htonl(INADDR_ANY) ?
                                    "--" : addrtoname(AF_INET, &dst),
                                    ntohs(nip->n_tag),
                                    mval,
                                    (mval == HOPCNT_INFINITY ?
                                        " (not reachable)" : ""));
                        }
                        break;

                case RIPCMD_POLLENTRY:
                        if (len < sizeof (*ep))
                                break;
                        len -= sizeof (*ep);
                        ep = (const struct entryinfo *)rip->rip_nets;
                        /* LINTED */
                        sin = (const struct sockaddr_in *)&ep->rtu_dst;
                        (void) snprintf(get_line(0, 0), get_line_remain(),
                            "Destination = %s %s",
                            inet_ntoa(sin->sin_addr),
                            addrtoname(AF_INET, (void *)&sin->sin_addr));
                        /* LINTED */
                        sin = (const struct sockaddr_in *)&ep->rtu_router;
                        (void) snprintf(get_line(0, 0), get_line_remain(),
                            "Router      = %s %s",
                            inet_ntoa(sin->sin_addr),
                            addrtoname(AF_INET, (void *)&sin->sin_addr));
                        (void) snprintf(get_line(0, 0), get_line_remain(),
                            "Flags = %4x", (unsigned)ep->rtu_flags);
                        (void) snprintf(get_line(0, 0), get_line_remain(),
                            "State = %d", ep->rtu_state);
                        (void) snprintf(get_line(0, 0), get_line_remain(),
                            "Timer = %d", ep->rtu_timer);
                        (void) snprintf(get_line(0, 0), get_line_remain(),
                            "Metric = %d", ep->rtu_metric);
                        (void) snprintf(get_line(0, 0), get_line_remain(),
                            "Int flags = %8x", ep->int_flags);
                        (void) snprintf(get_line(0, 0), get_line_remain(),
                            "Int name = \"%.*s\"", sizeof (ep->int_name),
                            ep->int_name);
                        break;

                case RIPCMD_SEC_RESPONSE:
                case RIPCMD_SEC_T_RESPONSE:
                        if (len < sizeof (rip->rip_tsol.rip_generation))
                                break;
                        len -= sizeof (rip->rip_tsol.rip_generation);
                        show_space();
                        (void) snprintf(get_line(0, 0), get_line_remain(),
                            "Generation = %u",
                            (unsigned)ntohl(rip->rip_tsol.rip_generation));
                        rsep = rip->rip_tsol.rip_sec_entry;
                        (void) snprintf(get_line(0, 0), get_line_remain(),
                            "Address         E-METRIC");
                        rsep = rip->rip_tsol.rip_sec_entry;
                        while (len > 0) {
                                char *cp;
                                int blen, num;

                                rsn = rip_next_sec_entry(rsep, len);
                                if (rsn == NULL)
                                        break;
                                dst.s_addr = rsep->rip_dst;
                                cp = get_line(0, 0);
                                blen = get_line_remain();
                                (void) snprintf(cp, blen, "%-16s ",
                                    inet_ntoa(dst));
                                cp += 17;
                                blen -= 17;
                                rep = rsep->rip_emetric;
                                for (count = ntohl(rsep->rip_count); count > 0;
                                    count--) {
                                        (void) snprintf(cp, blen, "metric=%d",
                                            ntohs(rep->rip_metric));
                                        blen -= strlen(cp);
                                        cp += strlen(cp);
                                        tokp = rep->rip_token;
                                        num = get_numtokens(
                                            ntohs(rep->rip_mask));
                                        /* advance to the next emetric */
                                        rep = (const struct rip_emetric *)
                                            &rep->rip_token[num];
                                        if (num > 0) {
                                                (void) snprintf(cp, blen,
                                                    ",tokens=%lx",
                                                    (long)ntohl(*tokp));
                                                tokp++;
                                                num--;
                                        } else {
                                                (void) strlcpy(cp, ",no tokens",
                                                    blen);
                                        }
                                        while (num > 0) {
                                                blen -= strlen(cp);
                                                cp += strlen(cp);
                                                (void) snprintf(cp, blen,
                                                    ",%lx",
                                                    (long)ntohl(*tokp));
                                                tokp++;
                                                num--;
                                        }
                                        blen -= strlen(cp);
                                        cp += strlen(cp);
                                }
                                if (rsep->rip_count == 0) {
                                        (void) strlcpy(cp,
                                            "NULL (not reachable)", blen);
                                }
                                len -= (const char *)rsn - (const char *)rsep;
                                rsep = rsn;
                        }
                        break;

                case RIPCMD_TRACEON:
                case RIPCMD_TRACEOFF:
                        (void) snprintf(get_line(0, 0), get_line_remain(),
                            "Trace file = %.*s", len, rip->rip_tracefile);
                        len = 0;
                        break;
                }
        }

        return (fraglen - len);
}

static const char *
show_cmd(int c)
{
        switch (c) {
        case RIPCMD_REQUEST:
                return ("route request");
        case RIPCMD_RESPONSE:
                return ("route response");
        case RIPCMD_TRACEON:
                return ("route trace on");
        case RIPCMD_TRACEOFF:
                return ("route trace off");
        case RIPCMD_POLL:
                return ("route poll");
        case RIPCMD_POLLENTRY:
                return ("route poll entry");
        case RIPCMD_SEC_RESPONSE:
                return ("route sec response");
        case RIPCMD_SEC_T_RESPONSE:
                return ("route sec_t response");
        }
        return ("?");
}

static int
get_numtokens(unsigned int mask)
{
        int num = 0;

        while (mask != 0) {
                num++;
                mask &= mask - 1;
        }
        return (num);
}

static const struct rip_sec_entry *
rip_next_sec_entry(const struct rip_sec_entry *rsep, int len)
{
        const struct rip_emetric *rep;
        const char *limit = (const char *)rsep + len;
        long count;

        if ((const char *)(rep = rsep->rip_emetric) > limit)
                return (NULL);
        count = ntohl(rsep->rip_count);
        while (count > 0) {
                if ((const char *)rep->rip_token > limit)
                        return (NULL);
                rep = (struct rip_emetric *)
                    &rep->rip_token[get_numtokens(ntohs(rep->rip_mask))];
                if ((const char *)rep > limit)
                        return (NULL);
                count--;
        }
        return ((const struct rip_sec_entry *)rep);
}