1
0
Files
irix-657m-src/irix/cmd/netman/lib/pr_eval.c
2022-09-29 17:59:04 +03:00

379 lines
8.3 KiB
C

/*
* Copyright 1990 Silicon Graphics, Inc. All rights reserved.
*
* Evaluate a packet data expression in a protocol context.
* NB: eval knows that ex_kid is equivalent to ex_right.
*/
#include "datastream.h"
#include "debug.h"
#include "expr.h"
#include "protocol.h"
#include "protostack.h"
#include "scope.h"
#include "snooper.h"
extern long _now;
/*
* Invariant arguments to recursive eval and test.
*/
typedef struct {
Protocol *proto;
DataStream *stream;
ProtoStack *stack;
} EvalData;
static ExprError *evalerr;
static int eval(Expr *, EvalData *, Expr *);
static int test(Expr *, EvalData *);
static int mismatch(Expr *, Expr *);
int
pr_eval(Protocol *pr, Expr *ex, SnoopPacket *sp, int len, Snooper *sn,
ExprError *err, Expr *rex)
{
EvalData ed;
DataStream ds;
ProtoStack ps;
_now = sp->sp_timestamp.tv_sec;
ed.proto = pr;
ds_init(&ds, sp->sp_data + sn->sn_rawhdrpad, len - sn->sn_rawhdrpad,
DS_DECODE, pr->pr_byteorder);
ed.stream = &ds;
PS_INIT(&ps, sn, &sp->sp_hdr);
ed.stack = &ps;
evalerr = err;
ex_null(rex);
return eval(ex, &ed, rex);
}
/*
* Evaluate a boolean expression given a protocol and packet data starting
* with that protocol's frame.
* XXX ExprError * result parameter?
*/
int
pr_test(Protocol *pr, Expr *ex, SnoopPacket *sp, int len, Snooper *sn)
{
EvalData ed;
DataStream ds;
ProtoStack ps;
ExprError err;
_now = sp->sp_timestamp.tv_sec;
ed.proto = pr;
ds_init(&ds, sp->sp_data + sn->sn_rawhdrpad, len - sn->sn_rawhdrpad,
DS_DECODE, pr->pr_byteorder);
ed.stream = &ds;
PS_INIT(&ps, sn, &sp->sp_hdr);
ed.stack = &ps;
evalerr = &err;
return test(ex, &ed);
}
/*
* Evaluate an expression into a number, an address, or a string.
*/
static int
eval(Expr *ex, EvalData *ed, Expr *rex)
{
switch (ex->ex_op) {
case EXOP_OR:
/*
* Short-circuiting logical or.
*/
rex->ex_op = EXOP_NUMBER;
rex->ex_val = test(ex->ex_left, ed) || test(ex->ex_right, ed);
break;
case EXOP_AND:
/*
* Short-circuiting logical and.
*/
rex->ex_op = EXOP_NUMBER;
rex->ex_val = test(ex->ex_left, ed) && test(ex->ex_right, ed);
break;
case EXOP_ARRAY: {
Expr tex;
int index;
Protocol *pr;
DataStream ds;
/*
* Evaluate the index expression.
*/
if (!eval(ex->ex_right, ed, &tex))
return 0;
assert(tex.ex_op == EXOP_NUMBER);
index = tex.ex_val;
/*
* Evaluate the array lvalue. After recurring through some
* number of array and struct nodes, this call will evaluate
* an array field, returning its element type field (strings
* are a special case).
*/
if (!eval(ex->ex_left, ed, &tex))
return 0;
if (tex.ex_op == EXOP_STRING) {
if (index >= (unsigned)tex.ex_str.s_len)
return 0;
rex->ex_op = EXOP_NUMBER;
rex->ex_val = tex.ex_str.s_ptr[index];
break;
}
assert(tex.ex_op == EXOP_FIELD);
/*
* Set the element type field's cookie to the desired index
* and fetch the rvalue.
*/
tex.ex_field->pf_cookie = index;
pr = ed->proto;
ds_init(&ds, ed->stream->ds_next, ed->stream->ds_count,
DS_DECODE, pr->pr_byteorder);
if (!pr_fetch(pr, tex.ex_field, &ds, ed->stack, rex))
return 0;
break;
}
case EXOP_STRUCT: {
Expr tex;
DataStream *ds;
int tell;
/*
* Evaluate the structure-valued expression on the left.
* The underlying protocol returns the struct's field in tex,
* with its cookie set to the stream offset of the beginning
* of the struct relative to protocol origin.
*/
if (!eval(ex->ex_left, ed, &tex))
return 0;
assert(tex.ex_op == EXOP_FIELD);
assert(tex.ex_field->pf_type == EXOP_STRUCT);
/*
* Seek to the struct base and evaluate the right part, which
* may be a field, an array index expression, or another struct
* member expression.
*/
ds = ed->stream;
tell = DS_TELL(ds);
if (!ds_seek(ds, tex.ex_field->pf_cookie, DS_RELATIVE)
|| !eval(ex->ex_right, ed, rex)) {
(void) ds_seek(ds, tell, DS_ABSOLUTE);
return 0;
}
(void) ds_seek(ds, tell, DS_ABSOLUTE);
break;
}
case EXOP_NOT:
/*
* Logical not must call test rather than ex_operate/ex_test
* in order to handle protocol, array, and struct matching.
*/
rex->ex_op = EXOP_NUMBER;
rex->ex_val = !test(ex->ex_kid, ed);
break;
case EXOP_PROTOCOL: {
Protocol *pr;
DataStream ds;
/*
* Clone ed->stream with pr's byte order. Check for the
* snoop pseudo-protocol, which we need never match, but
* skip instead via ex_match. For other protocols, call
* pr_match for all but the last in a path, and for the
* last too if it has no discriminant.
*/
pr = ed->proto;
ds_init(&ds, ed->stream->ds_next, ed->stream->ds_count,
DS_DECODE, pr->pr_byteorder);
if (ex->ex_prsym->sym_proto == &snoop_proto) {
if (!ex_match(ex, &ds, ed->stack, rex))
return mismatch(ex, rex);
} else if (ex->ex_member || pr->pr_discriminant == 0) {
if (!pr_match(pr, ex, &ds, ed->stack, rex))
return mismatch(ex, rex);
} else {
if (!pr_fetch(pr, pr->pr_discriminant, &ds, ed->stack,
rex)) {
return mismatch(ex, rex);
}
rex->ex_op = EXOP_NUMBER;
rex->ex_val =
(rex->ex_val == ex->ex_prsym->sym_prototype);
}
break;
}
case EXOP_FIELD: {
Protocol *pr;
DataStream ds;
pr = ed->proto;
ds_init(&ds, ed->stream->ds_next, ed->stream->ds_count,
DS_DECODE, pr->pr_byteorder);
if (!pr_fetch(pr, ex->ex_field, &ds, ed->stack, rex))
return 0;
break;
}
case EXOP_FETCH: {
DataStream ds;
/*
* Clone ed->stream, seek to the field's base, and fetch
* the specified number of bytes.
*/
ds_init(&ds, ed->stream->ds_next, ed->stream->ds_count,
DS_DECODE, ed->proto->pr_byteorder);
if (!ds_seek(&ds, ex->ex_off, DS_RELATIVE))
return 0;
switch (ex->ex_type) {
case EXOP_NUMBER:
if (!ds_int(&ds, &rex->ex_val, ex->ex_size,
DS_ZERO_EXTEND)) {
return 0;
}
rex->ex_op = EXOP_NUMBER;
break;
case EXOP_ADDRESS:
if (!ds_bytes(&ds, A_BASE(&rex->ex_addr, ex->ex_size),
ex->ex_size)) {
return 0;
}
rex->ex_op = EXOP_ADDRESS;
break;
case EXOP_STRING:
rex->ex_str.s_ptr = (char *) ds.ds_next;
if (!ds_seek(&ds, ex->ex_size, DS_RELATIVE))
return 0;
rex->ex_str.s_len = ex->ex_size;
rex->ex_op = EXOP_STRING;
}
break;
}
case EXOP_CALL: {
int argc;
Expr argv[MAXCALLARGS];
DataStream ds;
/*
* Evaluate any arguments, terminating the list if there are
* fewer than MAXCALLARGS with an EXOP_NULL node in argv, then
* call the protocol function.
*/
for (argc = 0; argc < MAXCALLARGS; argc++) {
if (ex->ex_args[argc] == 0) {
argv[argc].ex_op = EXOP_NULL;
break;
}
ex_null(&argv[argc]);
if (!eval(ex->ex_args[argc], ed, &argv[argc]))
return 0;
}
ds_init(&ds, ed->stream->ds_next, ed->stream->ds_count,
DS_DECODE, ed->proto->pr_byteorder);
if (!(*ex->ex_func)(argv, &ds, ed->stack, rex))
return 0;
break;
}
case EXOP_NUMBER:
case EXOP_ADDRESS:
case EXOP_STRING:
*rex = *ex;
break;
default: {
Expr *lex, left; /* left kid's result if binary */
assert(ex->ex_arity != EX_NULLARY);
switch (ex->ex_arity) {
case EX_BINARY:
lex = &left;
ex_null(lex);
if (!eval(ex->ex_left, ed, lex))
return 0;
break;
case EX_UNARY:
lex = 0;
}
if (!eval(ex->ex_kid, ed, rex))
return 0;
if (ex_operate(rex, ex->ex_op, lex, rex, evalerr)
!= ET_SIMPLE) {
return 0;
}
}
}
return 1;
}
static int
test(Expr *ex, EvalData *ed)
{
Expr rex;
enum exop type;
ex_null(&rex);
if (!eval(ex, ed, &rex))
return 0;
switch (rex.ex_op) {
case EXOP_PROTOCOL:
return 1;
case EXOP_FIELD:
type = rex.ex_field->pf_type;
return (type == EXOP_ARRAY || type == EXOP_STRUCT);
default:
return ex_test(&rex);
}
}
static int
mismatch(Expr *pex, Expr *rex)
{
for (;;) {
pex = pex->ex_member;
if (pex == 0) {
rex->ex_op = EXOP_NUMBER;
rex->ex_val = 0;
return 1;
}
if (pex->ex_op != EXOP_PROTOCOL)
return 0;
}
/* NOTREACHED */
}
int
ex_match(Expr *pex, DataStream *ds, ProtoStack *ps, Expr *rex)
{
Protocol *pr;
Expr *ex;
EvalData ed;
pr = pex->ex_prsym->sym_proto;
ex = pex->ex_member;
if (ex == 0) {
if (pr->pr_flags & PR_MATCHNOTIFY)
(void) pr_match(pr, 0, ds, ps, rex);
rex->ex_op = EXOP_NUMBER;
rex->ex_val = 1;
return 1;
}
ed.proto = pr;
ed.stream = ds;
ed.stack = ps;
return eval(ex, &ed, rex);
}