RFC1076 - HEMS monitoring and control language(2)

时间:2005-02-12 来源: 作者: 点击:
an error for there to be multiple matches; actual experience is needed to decide.) GET array template filter GET array For each item in array that matches filter, fill in the template with values fro
  
an error for there to be multiple matches; actual
experience is needed to decide.)

GET array template filter GET array
For each item in <array> that matches <filter>, fill in the
template with values from the data tree and emit the
result. The first tag of <template> must be equal to the
iteration tag. Selected parts of matched items are emitted
based upon <template>, just as in a non-filtered GET
operation.

GET-ATTRIBUTES
array template filter GET-ATTRIBUTES array
Same as GET, except emit attributes rather than data
values.

SET array value filter SET array
Same as GET, except set the values in <value> rather than
retrieving values. Several values in the data tree will be
changed if the filter matches more than one item in the
array.

DELETE array filter DELETE array
Delete the entry(s) in <array> that match <filter>.

Notes about filter execution:

- Expressions are executed by inorder tree traversal.

- Since the filter operations are all GETs and comparisons,
there are no side-effects to filter execution, so an
implementation is free to execute only as much of the
filter as required to produce a result (e.g., don't execute
the rest of an AND if the first comparison turns out to be
false).

- It is not an error for a filter to test a data item that
isn't in the data tree. In this situation, the comparison
just fails (is false). This means that filters don't need
to test for the existence of optional data before
attempting to compare it.

Here is an example of how filtering would be used to obtain the input
and output packet counts for the interface with IP address 10.0.0.51.

Interfaces BEGIN -- dictionary
InterfaceData{ pktsIn, pktsOut } -- template
Filter{ equal{ address(10.0.0.51) } }
GET
END -- finished with dict

The returned value would be something like:

Interfaces{ -- BEGIN
InterfaceData{ pktsIn(1345134), pktsOut(1023729) }
-- GET
} -- END

The annotations indicate which part of the response is generated by
the different operators in the query.

Here is an example of accessing a table contained within some other
table. Suppose we want to get at the ARP table for the interface
with IP address 36.8.0.1 and retrieve the entire ARP entry for the
host with IP address 36.8.0.23. In order to retrieve a single entry
in the ARP table (using a filtered GET), a BEGIN must be used to get
down to the ARP table. Since the ARP table is contained within the
Interfaces dictionary (an array), a filtered BEGIN must be used.

Interfaces BEGIN -- dictionary
InterfaceData{ ARP } -- path
Filter{ equal{ address(36.8.0.1) } } -- filter
BEGIN -- filtered BEGIN

-- Now in ARP table for 38.0.0.1; get entry for 38.8.0.23.
addrMap -- whole entry
Filter{ equal{ ipAddr(36.8.0.23) } } -- filter
GET -- filtered GET
END
END

The result would be:

Interfaces{ -- first BEGIN
InterfaceData{ ARP{ -- second BEGIN
addrMap{ ipAddr(36.8.0.23), physAddr(..) } -- from GET
} } -- first END
} -- second END

Note which parts of the output are generated by different parts of
the query.

Here is an example of how the SET operator would be used to shut down
the interface with ip-address 10.0.0.51 by changing its status to
"down".

Interfaces BEGIN -- get dictionary
Interface{ Status(down) } -- value to set
Filter{ equal{ IP-addr(10.0.0.51) } }
SET
END

If the SET is successful, the result would be:

Interfaces{ -- BEGIN
Interface{ Status(down) } -- from SET
} -- END

8.7 Terminating a Query

A query is implicitly terminated when there are no more ASN.1 objects
to be processed by the interpreter. For a perfectly-formed query,
the interpreter would be back in the state it was when it started:
the stack would have only the root dictionary on it, and all of the
ASN.1 objects in the result would be terminated.

If there are still "open" ASN.1 objects in the result (caused by
leaving ENDs off of the query), then these are closed, as if a
sufficient number of ENDs were provided. This condition would be
indicated by the existence of dictionaries other than the root
dictionary on the stack.

If an extra END is received that would pop the root dictionary off of
the stack, the query is terminated immediately. No error is
generated.

9. EXTENDING THE SET OF VALUES

There are two ways to extend the set of values understood by the
query language. The first is to register the data and its meaning
and get an ASN.1 tag assigned for it. This is the preferred method
because it makes that data specification available for everyone to
use.

The second method is to use the VendorSpecific application type to
"wrap" the vendor-specific data. Wherever an implementation defines
data that is not in RFC-1024, the "VendorSpecific" tag should be used
to label a dictionary containing the vendor-specific data. For
example, if a vendor had some data associated with interfaces that
was too strange to get standard numbers assigned for, they could,
instead represent the data like this:

interfaces {
interface {
in-pkts, out-pkts, ...
VendorSpecific { ephemeris, declination }
}
}

In this case, ephemeris and declination correspond to two context-
dependent tags assigned by the vendor for their non-standard data.

If the vendor-specific method is chosen, the private data MUST have
descriptions available through the GET-ATTRIBUTES operator. Even
with this descriptive ability, the preferred method is to get
standard numbers assigned if possible.

10. AUTHORIZATION

This specification does not state what type of authorization system
is used, if any. Different systems may have needs for different
mechanisms (authorization levels, capability sets, etc.), and some
systems may not care about authorization at all. The only effect
that an authorization system has on a query is to restrict what data
items in the tree may be retrieved or modified.

Therefore, there are no explicit query language features that deal
with protection. Instead, protection mechanisms are implicit and may
make some of the data invisible (for GET) or non-writable (for SET):

- Each query runs with some level of authorization or set of
capabilities, determined by its environment (HEMS and the
HEMP header).

- Associated with each data item in the data tree is some
sort of test to determine if a query's authorization should
grant it access to the item.

Authorization tests are only applied to query language operations
that retrieve information (GET, GET-ATTRIBUTES, and GET-RANGE) or
modify it (SET, CREATE, DELETE). An authorization system must not
affect the operation of BEGIN and END. In particular, the
authorization must not hide entire dictionaries, because that would
make a BEGIN on such a dictionary fail, terminating the entire query.

11. ERRORS

If some particular information is requested but is not available, it
will be returned as "no-value" by giving the ASN.1 length as 0.

When there is any other kind of error, such as having improper
arguments on the top of the stack or trying to execute BEGIN when the
path doesn't refer to a dictionary, an ERROR object is emitted.

The contents of this object identify the exact nature of the error:

Error ::= [APPLICATION 0] IMPLICIT SEQUENCE {
errorCode INTEGER,
errorInstance INTEGER,
errorOffset INTEGER
errorDescription IA5String,
errorOp INTEGER,
}

errorCode identifies what the error was, and errorInstance is an
implementation-dependent code that gives a more precise indication of
where the error occured. errorOffset is the location within the
query where the error occurred. If an operation was being executed,
errorOp contains its operation code, otherwise zero.
errorDescription is a text string that can be printed that gives some
description of the error. It will at least describe the errorCode,
but may also give details implied by errorInstance. Detailed
definitions of all of the fields are given in appendix I.2.

Since there may be several unterminated ASN.1 objects in progress at
the time the error occurs, each one must be terminated. Each
unterminated object will be closed with a copy of the ERROR object.
Depending upon the type of length encoding used for this object, this

will involve filling the value for the length (definite length form)
or emitting two zero octets (indefinite length form). After all
objects are terminated, a final copy of the ERROR object will be
emitted. This structure guarantees that the error will be noticed at
every level of interpretation on the receiving end.

In summary, if there was an error before any ASN.1 objects were
generated, then the result would simply be:
error{...}

If a couple of ASN.1 objects were unterminated when the error
occurred, the result might look like:
interfaces{
interface { name(...) type(...) error{...} }
error{...}
}
error{...}

It would be possible to define a "WARNING" object that has a similar
(or same) format as ERROR, but that would be used to annotate
responses when a non-fatal "error" occurs, such as attempting to
SET/CREATE/DELETE and the operation is denied. This would be an
additional complication, and we left it out in the interests of
simplicity.

I. ASN.1 DESCRIPTIONS OF QUERY LANGUAGE COMPONENTS

A query consists of a sequence of ASN.1 objects, as follows:

Query := IMPLICIT SEQUENCE of QueryElement;

QueryElement ::= CHOICE {
Operation,
Filter,
Template,
Path,
InputValue
}

Operation and Filter are defined below. The others are:

Template ::= any
Path ::= any
InputValue ::= any

These three are all similar, but have different restrictions on their
structure:

Template Specifies a portion of the tree, naming one or more
values, but not containing any values.

Path Specifies a single path from one point in the tree to
another, naming exactly one value, but not containing
a value.

InputValue Gives a value to be used by a query language
operator.

A query response consists of a sequence of ASN.1 objects, as follows:

Response := IMPLICIT SEQUENCE of ResponseElement;

ResponseElement ::= CHOICE {
ResultValue,
Error
}

Error is defined below. The others are:

ResultValue ::= any

ResultValue is similar to Template, above:

ResultValue Specifies a portion of the tree, naming and
containing one or more values.

The distinctions between these are elaborated in section 6.

I.1 Operation Codes

Operation codes are all encoded in a single application-specific
type, whose value determines the operation to be performed. The
definition is:

Operation ::= [APPLICATION 1] IMPLICIT INTEGER {
reserved(0),
begin(1),
end(2),
get(3),
get-attributes(4),
get-range(5),
set(6),

create(7),
delete(8)
}

I.2 Error Returns

An Error object is returned within a reply when an error is
encountered during the processing of a query. Note that the
definition this object is similar to that of the HEMP protocol error
structure. The error codes have been selected to keep the code
spaces distinct between the two. This is intended to ease the
processing of error messages. See section 11 for more information.

Error ::= [APPLICATION 0] IMPLICIT SEQUENCE {
errorCode INTEGER,
errorInstance INTEGER,
errorOffset INTEGER
errorDescription IA5String,
errorOp INTEGER,
}

The fields are defined as follows:

errorCode Identifies the general cause of the error.

errorInstance An implementation-dependent code that gives a more
precise indication of where the error occured in the
query processor. This is most useful when internal
errors are reported.

errorOffset The location within the query where the error was
detected. The first octet of the query is numbered
zero.

errorOp If an operation was being executed, this contains its
operation code, otherwise zero.

errorDescription
A text string that can be printed that gives some
description of the error. It will at least describe
the errorCode, but may also give details implied by
errorInstance.

Some errors are associated with the execution of specific operations,
and others with the overall operation of the query interpreter. The
errorCodes are split into two groups.

The first group deals with overall interpreter operation. Except for

"unknown operation", these do not set errorOp.

100 Other error.
Any error not listed below.

101 Format error.
An error has been detected in the format of the input
stream, preventing further interpretation of the
query.

102 System error.
The query processor has failed in some way due to an
internal error.

103 Stack overflow.
Too many items were pushed on the stack.

104 Unknown operation.
The operation code is invalid. errorOp is set.

The second group is errors that are associated with the execution of
particular operations. errorOp will always be set for these.

200 Other operation error.
Any error, associated with an operation, not listed
below.

201 Stack underflow.
An operation expected to see some number of operands
on the stack, and there were fewer items on the
stack.

202 Operand error.
An operation expected to see certain operand types on
the stack, and something else was there.

203 Invalid path for BEGIN.
A path given for BEGIN was invalid, because some
element in the path didn't exist.

204 Non-dictionary for BEGIN.
A path given for BEGIN was invalid, because the given
node was a leaf node, not a dictionary.

205 BEGIN on array element.
The path specified an array element. The path must
point at a single, unique, node. A filtered BEGIN
should have been used.

206 Empty filter for BEGIN.
The filter for a BEGIN didn't match any array
element.

207 Filtered operation on non-array.
A filtered operation was attempted on a regular
dictionary. Filters can only be used on arrays.

208 Index out of bounds.
The starting address or length for a GET-RANGE
operation went outside the bounds for the given
object.

209 Bad object for GET-RANGE.
GET-RANGE can only be applied to objects whose base
type is OctetString.

This list is probably not quite complete, and would need to be
extended, based upon implementation experience.

I.3 Filters

Many of the operations can take a filter argument to select among
elements in an array. They are discussed in section 8.6.

Filter ::= [APPLICATION 2] CHOICE {
present [0] DataPath,
equal [1] DataValue,
greaterOrEqual [2] DataValue,
lessOrEqual [3] DataValue,
and [4] SEQUENCE OF Filter,
or [5] SEQUENCE OF Filter,
not [6] Filter
}

DataPath ::= ANY -- Path with no value

DataValue ::= ANY -- Single data value

A filter is executed by inorder traversal of its ASN.1 structure.

The basic filter operations are:

present tests for the existence of a particular data item in
the data tree

equal tests to see if the named data item is equal to the
given value.

greaterOrEqual tests to see if the named data item is greater than
or equal to the given value.

lessOrEqual tests to see if the named data item is less than or
equal to the given value.

These may be combined with "and", "or", and "not" operators to form
arbitrary boolean expressions. The "and" and "or" operators will
take any number of terms. Terms are only evaluated up to the point
where the outcome of the expression is determined (i.e., an "and"
term's value is false or an "or" term's value is true).

I.4 Attributes

One or more Attributes structure is returned by the GET-ATTRIBUTES
operator. This structure provides descriptive information about
items in the data tree. See the discussion in section 8.3.

Attributes ::= [APPLICATION 3] IMPLICIT SEQUENCE {
tagASN1 [0] IMPLICIT INTEGER,
valueFormat [1] IMPLICIT INTEGER,
longDesc [2] IMPLICIT IA5String OPTIONAL,
shortDesc [3] IMPLICIT IA5String OPTIONAL,
unitsDesc [4] IMPLICIT IA5String OPTIONAL,
precision [5] IMPLICIT INTEGER OPTIONAL,
properties [6] IMPLICIT BITSTRING OPTIONAL,
valueSet [7] IMPLICIT SET OF valueDesc OPTIONAL
}
valueDesc ::= IMPLICIT SEQUENCE {
value [0] ANY, -- Single data value
desc [1] IA5String
}

The meanings of the various attributes are given below.

tagASN1 The ASN.1 tag for this object. This attribute is
required.

valueFormat The underlying ASN.1 type of the object (e.g.,
SEQUENCE or OCTETSTRING or Counter). This is not
just the tag number, but the entire tag, as it would
appear in an ASN.1 object. As such, it includes the
class, which should be either UNIVERSAL or
APPLICATION. Applications receiving this should

ignore the constructor bit. This attribute is
required.

longDesc A potentially lengthy text description which fully
defines the object. This attribute is optional for
objects defined in this memo and required for
entity-specific objects.

shortDesc A short mnemonic string of less than 15 characters,
suitable for labeling the value on a display. This
attribute is optional.

unitsDesc A short string used for integer values to indicate
the units in which the value is measured (e.g., "ms",
"sec", "pkts", etc.). This attribute is optional.

precision For Counter objects, the value at which the Counter
will roll-over. Required for all Counter objects.

properties A bitstring of boolean properties of the object. If
the bit is on, it has the given property. This
attribute is optional. The bits currently defined
are:

0 If true, the difference between two values of
this object is significant. For example, the
changes of a packet count is always significant,
it always conveys information. In this case, the
0 bit would be set. On the other hand, the
difference between two readings of a queue length
may be meaningless.

1 If true, the value may be modified with SET,
CREATE, and DELETE. Applicability of CREATE and
DELETE depends upon whether the object is in an
array.

2 If true, the object is a dictionary, and a BEGIN
may be used on it. If false, the object is leaf
node in the data tree.

3 If true, the object is an array-type dictionary,
and filters may be used to traverse it. (Bit 2
will be true also.)

valueSet For data that is defined as an ASN.1 CHOICE type (an
enumerated type), this gives descriptions for each of
the possible values that the data object may assume.

Each valueDesc is a <value,description> pair. This
information is especially important for control
items, which are very likely to appear in
VendorSpecific dictionaries, exactly the situation
where descriptive information is needed.

I.5 VendorSpecific

See the discussion in section 9.

VendorSpecific ::= [APPLICATION 4] IMPLICIT SET
of ANY

II. IMPLEMENTATION HINTS

Although it is not normally in the spirit of RFCs to define an
implementation, the authors feel that some suggestions will be useful
to implementors of the query language. This list is not meant to be
complete, but merely to give some hints about how the authors imagine
that the query processor might be implemented efficiently.

- It should be understood that the stack is of very limited
depth. Because of the nature of the query language, it can
get only about 4 entries (for arguments) plus the depth of
the tree (up to one BEGIN per level in the tree). This
comes out to about a dozen entries in the stack, a modest
requirement.

- The stack is an abstraction -- it should be implemented
with pointers, not by copying dictionaries, etc.

- An object-oriented approach should make implementation
fairly easy. Changes to the "shape" if the data items
(which will certainly occur, early on) will also be easier
to make.

- Only a few "messages" need to be understood by objects. By
having pointers to action routines for each basic operation
(GET,SET,...) associated with each node in the tree, common
routines (e.g., emit a long integer located at address X)
can be shared, and special routines (e.g., set the interface
state for interface X) can be implemented in a common
framework. Higher levels need know nothing about what data
is being dealt with.

- Most interesting objects are dictionaries, each of which
can be implemented using pointers to the data and procedure
"hooks" to perform specific operations such as GET, SET,

filtering, etc.

- The hardest part is actually extracting the data from
existing TCP/IP implementations that weren't designed with
detailed monitoring in mind. Query processors interfacing
to a UNIX kernel will have to make many system calls in
order to extract some of the more intricate structures,
such as routing tables. This should be less of a problem
if a system is designed with easy monitoring as a goal.

A Skeletal Implementation

This section gives a rather detailed example of the core of a query
processor. This code has not been tested, and is intended only to
give implementors ideas about how to tackle some aspects of query
processor implementation with finesse, rather than brute force.

The suggested architecture is for each dictionary to have a
"traverse" routine associated with it, which is called when any sort
of operation has to be done on that dictionary. Most nodes will
share the same traversal routine, but array dictionaries will usually
have routines that know about whatever special lookup mechanisms are
required.

Non-dictionary nodes would have two routines, "action", and
"compare", which implement query language operations and filter
comparisons, respectively. Most nodes would share these routines.

For example, there should be one "action" routine that does query
language operations on 32-bit integers, and another that works on
16-bit integers, etc.

Any traversal procedure would take arguments like:

traverse(node, mask, op, filter)
Treenode node; /* generic node-in-tree */
ASN mask; /* internal ASN.1 form */
enum opset op; /* what to do */
Filter filter; /* zero if no filter */

enum opset { begin, get, set, create, delete, geta,
c_le, c_ge, c_eq, c_exist };

The traversal procedure is called whenever anything must be done
within a dictionary. The arguments are:

node the current dictionary.

mask is either the template, path, or value, depending
upon the operation being performed. The top-level
identifier of this object will be looked up in the
context of <node>.

op is the operation to be performed, either one of the
basic operations, or a filter operation.

filter is the filter to be applied, or zero if none. There
will be no filter when <op> is a filter-comparison
operation.

The general idea is that the traversal proc associated with a node
has all of the knowledge about how to get around in this subtree
encoded within it. Hopefully, this will be the only place this
knowledge is coded. Here is a skeleton of the "standard" traversal
proc, written mostly in C.

When the query processor needs to execute a "GET" operation, it would
just call:
traverse(current, template, GET, 0)

Notes about this example:

- This traversal routine handles either query language
operations (GET, SET, etc.) or low-level filter operations.
Separate routines could be defined for the two classes of
operations, but they do much of the same work.

- Dictionary nodes have a <traversal> proc defined.

- Leaf nodes have an <action> proc, which implement GET, SET,
GET-ATTRIBUTES, CREATE, and DELETE, and a <compare> proc,
which performs low-level filter comparisons.

- In the generic routine, the filter argument is unused,
because the generic routine isn't used for array
dictionaries, and only array dictionaries use filters.

- An ASN type contains the top level tag and a list of
sub-components.

- size(mask) takes an ASN.1 object and tells how many
sub-items are in it. Zero means that this is a simple
object.

- lookup(node, tag) looks up a tag in the given (tree)node,
returning a pointer to the node. If the tag doesn't exist

in that node, a pointer to a special node "NullItem" is
returned. NullItem looks like a leaf node and has procs
that perform the correct action for non-existent data.

- This example does not do proper error handling, or ASN.1
generation, both of which would require additional code in
this routine.

/*
* For op = GET/SET/etc, return:
* true on error, otherwise false.
* When op is a filter operation, return:
* the result of the comparison.
*/
int std_traverse(node, mask, op, filter)
Treenode node; /* current node */
ASN mask; /* internal ASN.1 form */
enum opset op; /* what to do */
Filter filter; /* unused in this routine */
{
ASN item;
Treenode target;
boolean rv = false;
extern Treenode NullItem;

if (filter != null) {
error(...);
return true;
}

target = lookup(node, mask.tag);

/* We are at the leaf of the template/path/value. */
if (size(mask) == 0)
switch (op)
{
case BEGIN:
/* non-existent node, or leaf node */
if (target == NullItem || target.traverse == 0) {
error(...);
return true;
}
else {
begin(node, mask.tag);
return false;
}

case GET: case SET: case GETA:

case GETR: case CREATE: case DELETE:
/* A leaf in the mask specifies entire directory.
For GET, traverse the entire subtree. */
if (target.traverse)
if (op == GET) {
foreach subnode in target
/* Need to test to not GET memory. */
rv |= (*target.traverse)
(target, subnode.tag, op, 0);
return rv;
}
else if (op == SET) /* no-op */
return false;
else if (op != GETA) {
error(...);
return true;
}
/* We're at a leaf in both the mask and the tree.
Just execute the operation.
*/
else {
if (op == BEGIN) { /* Can't begin on leaf */
error(...);
return true;
else
return (*target.action)(target, mask, op);
}
} /* else */

default: /* Comparison ops. */
return (*target.compare)(target, mask, op);
} /* switch */

/* We only get here if mask has structure. */

/* can't have multiple targets for BEGIN */
if (op == BEGIN && size(mask) != 1) {
error(...);
return true;
}
/* or for a single filter operation. */
if (op is comparison && size(mask) != 1) {
error(...);
return false;
}
/* Iterate over the components in mask */
foreach item in mask
{

if (target.traverse) /* traverse subtree. */
rv |= (*component.traverse)(component, item, op, 0);
else /* leaf node, at last. */
if (op is comparison)
return (*target.compare)(target, mask, op);
else
return (*target.action)(target, mask, op);
} /* foreach */

return rv;
} /* std_traverse */

Here is a bare skeleton of an array-type dictionary's traversal proc.

int array_traverse(node, mask, op, filter)
Treenode node; /* current node */
ASN mask; /* internal ASN.1 form */
enum opset op; /* what to do */
Filter filter; /* unused in this routine */
{
Treenode target;
boolean rv = false;
extern Treenode NullItem;

/* Didn't find that key. */
if (mask.tag != this array's iteration tag)
return false;

if (op == BEGIN && filter == null) {
error(...);
return 1;
}

/* The implementation of this loop is the major trick! */
/* Needs to stop after first filter success on BEGIN. */
foreach target in node {
if (filter == null || /* if no filter, or */
ExecFilter(target, filter)) /* if it succeeds */
rv |= (target.traverse*)(target, mask, op, 0);
}
} /* array_traverse */

Object-oriented programming languages, such as C++, Modula, and Ada,
are well suited to this style of implementation. There should be no
particular difficulty with using a conventional language such as C or
Pascal, however.

III. OBTAINING A COPY OF THE ASN.1 SPECIFICATION

Copies of ISO Standard ASN.1 (Abstract Syntax Notation 1) are
available from the following source. It comes in two parts; both are
needed:

IS 8824 -- Specification (meaning, notation)
IS 8825 -- Encoding Rules (representation)

They are available from:

Omnicom Inc.
115 Park St, S.E. (new address as of March, 1987)
Vienna, VA 22180
(703) 281-1135
------分隔线----------------------------
顶一下
(0)
0%
踩一下
(0)
0%
------分隔线----------------------------
最新评论 查看所有评论
发表评论 查看所有评论
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价:
表情:
用户名: 密码: 验证码:
推荐内容