/*
* 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.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#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);
}