362 lines
8.1 KiB
C
362 lines
8.1 KiB
C
/*
|
|
* Copyright 1988 Silicon Graphics, Inc. All rights reserved.
|
|
*
|
|
* Ethernet snoop module.
|
|
*/
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <net/if.h>
|
|
#include <netinet/in.h> /* for ntohs() */
|
|
#include <netinet/if_ether.h> /* for ETHERMTU */
|
|
#include "index.h"
|
|
#include "protodefs.h"
|
|
#include "strings.h"
|
|
#include "protocols/ether.h"
|
|
|
|
char ethername[] = "ether";
|
|
|
|
/*
|
|
* Ethernet field identifiers and descriptors.
|
|
*/
|
|
enum etherfid {
|
|
DST=ETHERFID_DST, SRC=ETHERFID_SRC, TYPE=ETHERFID_TYPE, LEN,
|
|
DISCRIM
|
|
};
|
|
|
|
static ProtoField etherfields[] = {
|
|
PFI_ADDR("dst", "Destination Address", DST, ETHERADDRLEN, PV_TERSE),
|
|
PFI_ADDR("src", "Source Address", SRC, ETHERADDRLEN, PV_TERSE),
|
|
PFI_UINT("type", "Packet Type", TYPE, u_short, PV_VERBOSE),
|
|
PFI("len", "Packet Length", LEN, (void*)DS_ZERO_EXTEND,
|
|
sizeof(u_short), 12, EXOP_NUMBER, PV_VERBOSE),
|
|
};
|
|
|
|
static ProtoField discrim =
|
|
PFI("discrim", "Type Discriminant", DISCRIM, (void*)DS_ZERO_EXTEND,
|
|
sizeof(u_short), 12, EXOP_NUMBER, PV_VERBOSE+1);
|
|
|
|
#define ETHERFID(pf) ((enum etherfid) (pf)->pf_id)
|
|
#define ETHERFIELD(fid) etherfields[(int) fid]
|
|
|
|
/*
|
|
* Ethernet macros.
|
|
*/
|
|
static ProtoMacro ethermacros[] = {
|
|
PMI("between", "src == $1 && dst == $2 || dst == $1 && src == $2"),
|
|
PMI("host", "src == $1 || dst == $1"),
|
|
PMI("multicast",
|
|
"(dst & 1:0:0:0:0:0) == 1:0:0:0:0:0 && dst != BROADCAST"),
|
|
};
|
|
|
|
static ProtOptDesc etheroptdesc[] = {
|
|
POD("hostbyname", ETHER_PROPT_HOSTBYNAME,
|
|
"Decode Ethernet addresses into hostnames"),
|
|
};
|
|
|
|
/*
|
|
* Ethernet protocol interface and operations.
|
|
*/
|
|
DefineProtocol(ether, ethername, "Ethernet", PRID_ETHER, DS_BIG_ENDIAN,
|
|
ETHERHDRLEN, &discrim, 0, etheroptdesc, lengthof(etheroptdesc),
|
|
0);
|
|
|
|
struct etheraddr etherbroadcastaddr = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
|
|
|
|
static Index *ethertypes;
|
|
static Protocol *llc, *idp;
|
|
static u_short llctype, idptype;
|
|
|
|
static int
|
|
ether_init()
|
|
{
|
|
if (!pr_register(ðer_proto, etherfields, lengthof(etherfields),
|
|
lengthof(ethermacros) + 100)) {
|
|
return 0;
|
|
}
|
|
pr_addmacros(ðer_proto, ethermacros, lengthof(ethermacros));
|
|
pr_addconstaddress(ðer_proto, "BROADCAST",
|
|
(char *) ðerbroadcastaddr, sizeof etherbroadcastaddr);
|
|
pr_addconstnumber(ðer_proto, "MTU", ETHERMTU);
|
|
in_create(3, sizeof(u_short), 0, ðertypes);
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
ether_setopt(int id, char *val)
|
|
{
|
|
extern int _etherhostbyname;
|
|
|
|
if ((enum ether_propt) id != ETHER_PROPT_HOSTBYNAME)
|
|
return 0;
|
|
_etherhostbyname = (*val != '0');
|
|
return 1;
|
|
}
|
|
|
|
/* ARGSUSED */
|
|
static void
|
|
ether_embed(Protocol *pr, long prototype, long qualifier)
|
|
{
|
|
u_short type;
|
|
|
|
if (pr->pr_id == PRID_LLC) {
|
|
llc = pr, llctype = prototype;
|
|
return;
|
|
}
|
|
if (pr->pr_id == PRID_IDP)
|
|
idp = pr, idptype = prototype;
|
|
|
|
type = prototype;
|
|
in_enter(ethertypes, &type, pr);
|
|
}
|
|
|
|
/* ARGSUSED */
|
|
Expr *
|
|
ether_resolve(char *name, int len, struct snooper *sn)
|
|
{
|
|
struct etheraddr *ea;
|
|
struct etheraddr addr;
|
|
Expr *ex;
|
|
|
|
ea = ether_hostaddr(name);
|
|
if (ea == 0) {
|
|
int n, v[5];
|
|
unsigned long b;
|
|
char *bp, *ep;
|
|
unsigned char *vp;
|
|
|
|
n = 0;
|
|
bp = name;
|
|
for (;;) {
|
|
b = strtoul(bp, &ep, 16);
|
|
if (bp == ep || b > 255)
|
|
return 0;
|
|
v[n++] = b;
|
|
if (*ep == '\0')
|
|
break;
|
|
if (*ep != ':' || n == 6)
|
|
return 0;
|
|
bp = ep + 1;
|
|
}
|
|
ea = &addr;
|
|
vp = &ea->ea_vec[ETHERADDRLEN];
|
|
while (--n >= 0)
|
|
*--vp = v[n];
|
|
while (vp > &ea->ea_vec[0])
|
|
*--vp = 0;
|
|
}
|
|
ex = expr(EXOP_ADDRESS, EX_NULLARY, name);
|
|
*A_CAST(&ex->ex_addr, struct etheraddr) = *ea;
|
|
return ex;
|
|
}
|
|
|
|
static ExprType
|
|
ether_compile(ProtoField *pf, Expr *mex, Expr *tex, ProtoCompiler *pc)
|
|
{
|
|
enum etherfid fid;
|
|
|
|
fid = ETHERFID(pf);
|
|
switch (fid) {
|
|
case DST:
|
|
case SRC: {
|
|
u_char *mask;
|
|
|
|
if (mex == 0)
|
|
mask = (u_char *) PC_ALLBYTES;
|
|
else {
|
|
if (mex->ex_op != EXOP_ADDRESS) {
|
|
pc_badop(pc, mex);
|
|
return ET_ERROR;
|
|
}
|
|
mask = A_BASE(&mex->ex_addr, pf->pf_size);
|
|
}
|
|
if (tex->ex_op != EXOP_ADDRESS) {
|
|
pc_badop(pc, tex);
|
|
return ET_ERROR;
|
|
}
|
|
if (!pc_bytefield(pc, pf, (char *) mask,
|
|
(char *) A_BASE(&tex->ex_addr, pf->pf_size),
|
|
ETHERHDRLEN)) {
|
|
return ET_COMPLEX;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case TYPE:
|
|
case DISCRIM: {
|
|
long mask, match;
|
|
|
|
if (!pc_intmask(pc, mex, &mask))
|
|
return ET_ERROR;
|
|
switch (tex->ex_op) {
|
|
case EXOP_PROTOCOL:
|
|
match = tex->ex_prsym->sym_prototype;
|
|
if (fid == DISCRIM
|
|
&& (match == llctype || match == idptype)) {
|
|
mask &= ~0x05FF;
|
|
match = 0;
|
|
}
|
|
break;
|
|
case EXOP_NUMBER:
|
|
match = tex->ex_val;
|
|
break;
|
|
default:
|
|
pc_badop(pc, tex);
|
|
return ET_ERROR;
|
|
}
|
|
if (!pc_intfield(pc, pf, mask, match, ETHERHDRLEN))
|
|
return ET_COMPLEX;
|
|
break;
|
|
}
|
|
|
|
case LEN: {
|
|
long mask, match;
|
|
|
|
if (!pc_intmask(pc, mex, &mask))
|
|
return ET_ERROR;
|
|
if (tex->ex_op == EXOP_NUMBER)
|
|
match = tex->ex_val;
|
|
else {
|
|
pc_badop(pc, tex);
|
|
return ET_ERROR;
|
|
}
|
|
if (!pc_intfield(pc, pf, mask, match, ETHERHDRLEN))
|
|
return ET_COMPLEX;
|
|
}
|
|
}
|
|
return ET_SIMPLE;
|
|
}
|
|
|
|
#define NETWARE_IDP_LOOKAHEAD(ds) \
|
|
(ds->ds_count > 1 && ds->ds_next[0] == 0xFF && ds->ds_next[1] == 0xFF)
|
|
|
|
static int
|
|
ether_match(Expr *pex, DataStream *ds, ProtoStack *ps, Expr *rex)
|
|
{
|
|
struct etherhdr *eh;
|
|
struct etherframe ef;
|
|
int matched;
|
|
|
|
eh = (struct etherhdr *) ds_inline(ds, sizeof *eh, sizeof(u_short));
|
|
if (eh == 0)
|
|
return 0;
|
|
if (ntohs(eh->eh_type) <= ETHERMTU) {
|
|
if (NETWARE_IDP_LOOKAHEAD(ds)) {
|
|
if (pex->ex_prsym->sym_proto != idp)
|
|
return 0;
|
|
} else {
|
|
if (pex->ex_prsym->sym_proto != llc) {
|
|
/* Eat llc and keep trying */
|
|
PS_PUSH(ps, &ef.ef_frame, ðer_proto);
|
|
ef.ef_src = eh->eh_src;
|
|
ef.ef_dst = eh->eh_dst;
|
|
matched = pr_match(llc, pex, ds, ps, rex);
|
|
PS_POP(ps);
|
|
return matched;
|
|
}
|
|
}
|
|
} else {
|
|
if (ntohs(eh->eh_type) != pex->ex_prsym->sym_prototype)
|
|
return 0;
|
|
}
|
|
PS_PUSH(ps, &ef.ef_frame, ðer_proto);
|
|
ef.ef_src = eh->eh_src;
|
|
ef.ef_dst = eh->eh_dst;
|
|
matched = ex_match(pex, ds, ps, rex);
|
|
PS_POP(ps);
|
|
return matched;
|
|
}
|
|
|
|
/* ARGSUSED */
|
|
static int
|
|
ether_fetch(ProtoField *pf, DataStream *ds, ProtoStack *ps, Expr *rex)
|
|
{
|
|
struct etherhdr *eh;
|
|
u_short type;
|
|
|
|
eh = (struct etherhdr *) ds_inline(ds, sizeof *eh, sizeof(u_short));
|
|
if (eh == 0)
|
|
return 0;
|
|
switch (ETHERFID(pf)) {
|
|
case DST:
|
|
*A_CAST(&rex->ex_addr, struct etheraddr) = eh->eh_dst;
|
|
rex->ex_op = EXOP_ADDRESS;
|
|
break;
|
|
case SRC:
|
|
*A_CAST(&rex->ex_addr, struct etheraddr) = eh->eh_src;
|
|
rex->ex_op = EXOP_ADDRESS;
|
|
break;
|
|
case TYPE:
|
|
case LEN:
|
|
rex->ex_val = ntohs(eh->eh_type);
|
|
rex->ex_op = EXOP_NUMBER;
|
|
break;
|
|
case DISCRIM:
|
|
type = ntohs(eh->eh_type);
|
|
if (type <= ETHERMTU)
|
|
type = NETWARE_IDP_LOOKAHEAD(ds) ? idptype : llctype;
|
|
rex->ex_val = type;
|
|
rex->ex_op = EXOP_NUMBER;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static void
|
|
ether_decode(DataStream *ds, ProtoStack *ps, PacketView *pv)
|
|
{
|
|
struct etherhdr *eh;
|
|
Protocol *pr;
|
|
struct etherframe ef;
|
|
|
|
eh = (struct etherhdr *) ds_inline(ds, sizeof *eh, sizeof(u_short));
|
|
if (eh == 0)
|
|
return;
|
|
eh->eh_type = ntohs(eh->eh_type);
|
|
|
|
/*
|
|
* Show src before dst so that higher layer protocols' source and
|
|
* destination fields line up.
|
|
*/
|
|
pv->pv_off = ETHERFIELD(SRC).pf_off;
|
|
pv_showfield(pv, ÐERFIELD(SRC), &eh->eh_src,
|
|
"%-25.25s", ether_hostname(&eh->eh_src));
|
|
pv->pv_off = ETHERFIELD(DST).pf_off;
|
|
pv_showfield(pv, ÐERFIELD(DST), &eh->eh_dst,
|
|
"%-25.25s", ether_hostname(&eh->eh_dst));
|
|
pv->pv_off = ETHERFIELD(TYPE).pf_off;
|
|
|
|
/*
|
|
* If we can't decode the network protocol, then promote type's level
|
|
* to terse and try to print a description of it.
|
|
*/
|
|
if (eh->eh_type <= ETHERMTU) {
|
|
pv_showfield(pv, ÐERFIELD(LEN), &eh->eh_len,
|
|
"%-5u", eh->eh_len);
|
|
|
|
pr = NETWARE_IDP_LOOKAHEAD(ds) ? idp : llc;
|
|
} else {
|
|
pr = in_match(ethertypes, &eh->eh_type);
|
|
if (pr) {
|
|
pv_showfield(pv, ÐERFIELD(TYPE), &eh->eh_type,
|
|
"%-6s", pr->pr_name);
|
|
} else {
|
|
char *desc;
|
|
|
|
desc = ether_typedesc(eh->eh_type);
|
|
if (desc) {
|
|
pv_showfield(pv, ÐERFIELD(TYPE),
|
|
&eh->eh_type, "@t%-6s", desc);
|
|
} else {
|
|
pv_showfield(pv, ÐERFIELD(TYPE),
|
|
&eh->eh_type, "@t%#06x",
|
|
eh->eh_type);
|
|
}
|
|
}
|
|
}
|
|
|
|
PS_PUSH(ps, &ef.ef_frame, ðer_proto);
|
|
ef.ef_src = eh->eh_src;
|
|
ef.ef_dst = eh->eh_dst;
|
|
pv_decodeframe(pv, pr, ds, ps);
|
|
PS_POP(ps);
|
|
}
|