RFC 1023
HEMS monitoring and control language
HEMS Monitoring and Control Language ๐
RFC 1023 ๐ from the Network Working Group, October 1987 ๐
Written by G. Trewitt (Stanford) and C. Partridge (BBN/NNSC) โ๏ธ
Status: experimental, as the Status of This Memo section explains ๐งช
Obsoletes or updates: nothing is listed in the header ๐คทโโ๏ธ
Okay so this RFC lays out a monitoring and control language that's general-purpose but still efficient ๐ . The data inside an entity is modeled as a hierarchy, and you name a specific item by giving its path from the root of the tree ๐ณ. Most items are read-only, but some can be "set" to perform control operations ๐น๏ธ. Both requests and responses are represented using the ISO ASN.1 data encoding rules ๐ฅ.
Status of This Memo ๐
The whole point of this RFC is to give a spec for monitoring and controlling network entities on the Internet ๐. It's an experimental spec meant for testing the ideas in here ๐งช. Nothing in this memo is intended as an Internet standard at this time ๐ โโ๏ธ. After enough experimentation and discussion, this RFC will get redrafted, perhaps as a standard โจ. Distribution of this memo is unlimited ๐ฎ.
This language is a component of the High-Level Entity Monitoring System (HEMS), which is described in RFC-1021 and RFC-1022 ๐. Readers might wanna check those out while reading this one. RFC-1024 has the detailed assignments of numbers and structures used in this system ๐ข. This memo assumes you already know the ISO data encoding standard, ASN.1 ๐ง .
Overview and Scope ๐ฆ
So the basic model here is that you send a query to a monitored entity, and the entity sends a response back ๐จ. "Query" is used in the database sense ๐๏ธ. It might request info, modify stuff, or both ๐ซ. The examples are gateway-flavored, but this query-response mechanism can be applied to other entities besides just gateways ๐.
There's no interactive "conversation" like you'd get with SMTP [RFC-821] or FTP [RFC-959] ๐ โโ๏ธ. A query is a complete request that stands on its own and gets a complete response back ๐.
The monitored entity doesn't have to store the whole query ๐ง . It's totally possible for an implementation to process the query on the fly, producing portions of the response while the query is still being received โก.
Other HEMS RFCs: RFC-1021 is the Overview, RFC-1022 covers the transport protocol and message encapsulation, and RFC-1024 has the precise data definitions ๐. These issues aren't dealt with here ๐. It's assumed there's some mechanism to transport a sequence of octets to a query processor inside the monitored entity ๐, and some mechanism to return a sequence of octets to the entity making the query ๐.
Encoding of Queries and Responses ๐
Both queries and responses are encoded using the representation defined in ISO Standard ASN.1 (Abstract Syntax Notation 1) ๐. ASN.1 represents data as sequences of <tag,length,contents> triples, encoded as a stream of octets ๐งฉ. The data tuples can be recursively nested to represent structured data like arrays or records ๐ช. For a full description of this notation, check out the ISO documents IS 8824 and IS 8825 ๐. See the end of this memo for info about ordering these documents ๐.
Notation Used in This Proposal ๐
The notation in this memo is similar to ASN.1's, but less formal, smaller, and (hopefully) easier to read ๐ฅฐ. The biggest difference is that, in this memo, we are not concerned with the length of the data items ๐ โโ๏ธ.
ASN.1 data items can be either a "simple type" like integer or octet string, or a "structured type," which is a collection of data items ๐งบ. The notation:
ID(value)
represents a simple data item whose tag is "ID" with the given value. A structured data item is represented as:
ID { ... contents ... }
where contents is a sequence of data items. Remember that the contents may include both simple and structured types, so the structure is fully recursive ๐.
Sometimes you want to specify a type but give no value ๐ซฅ. That happens when there's no meaningful value for a particular measured parameter, or when the entire contents of a structured type is being specified. In that case, the same notation is used, but with the value omitted:
ID()
or
ID{}
The representation of this is obvious. The data item has zero for the length and no contents ๐.
Data Model ๐ง
Data in a monitored entity is modeled as a hierarchy ๐ณ. Implementations are not required to organize the data internally as a hierarchy, but they must provide this view of the data through the query language ๐ซถ. A hierarchy offers useful structure for the following operations:
Organization A hierarchy allows related data to be grouped
together in a natural way.
Naming The name of a piece of data is just the path from
the root to the data of interest.
Mapping onto ASN.1
ASN.1 can easily represent a hierarchy by using
"constructor" types as an envelope for an entire
subtree.
Efficient Representation
Hierarchical structures are quite compact and can
be traversed very quickly.
Each node in the hierarchy must have names for its component parts ๐ท๏ธ. We'd normally think of names as ASCII strings like "input errors," but the actual name is just an ASN.1 tag. Those tags are small integers (typically less than 100), so the monitored entity can easily map them onto its internal representation ๐ข.
We'll use the term "dictionary" to represent an internal node in the hierarchy ๐. Here's a possible organization of the hierarchy in an entity that has several network interfaces and multiple processes. The exact organization of data in entities is specified in RFC-1024 ๐ .
system {
name -- host name
clock-msec -- msec since boot
interfaces -- # of interfaces
}
interfaces { -- one per interface
interface { type, ip-addr, in-pkts, out-pkts, . . . }
interface { type, ip-addr, in-pkts, out-pkts, . . . }
interface { type, ip-addr, in-pkts, out-pkts, . . . }
:
}
processes {
process { name, stack, interrupts, . . . }
process { name, stack, interrupts, . . . }
:
}
route-table {
route-entry { dest, interface, nexthop, cost, . . . }
route-entry { dest, interface, nexthop, cost, . . . }
:
}
arp-table {
arp-entry { hard-addr, ip-addr, age }
arp-entry { hard-addr, ip-addr, age }
:
}
memory { }
The "name" of the clock in this entity would be:
system{ clock-msec }
and the name of a route-entry's IP address would be:
route-table{ route-entry{ ip-addr } }
Actually, this is the name of the IP addresses of ALL of the routing table entries ๐ฑ. This ambiguity is a problem in any situation where there are several instances of an item being monitored ๐. If there was a meaningful index for such tabular data (e.g., "routing table entry #1"), there would be no problem. Unfortunately, there usually isn't such an index ๐. The solution to this problem requires that the data be accessed on the basis of some of its content. More on this later ๐.
More than one piece of data can be named by a single ASN.1 object ๐. The entire collection of system information is named by:
system{ }
and the name of a routing table's IP address and cost would be:
route-table{ route-entry{ ip-addr, cost } }
Arrays ๐
There is one sub-type of a dictionary that is used as the basis for tables of objects with identical types. We call these dictionaries arrays ๐. In the example above, the dictionaries for interfaces, processes, routing tables, and ARP tables are all arrays. In fact, we expect that most of the interesting data in an entity will be contained in arrays โจ.
The primary difference between arrays and plain dictionaries is that arrays may contain only one type of item, while dictionaries, in general, will contain many different types of items ๐ง. Arrays are usually accessed associatively using special operators in the language ๐.
The fact that these objects are viewed externally as arrays does not mean they're represented in an implementation as linear lists of objects ๐ โโ๏ธ. Any collection of same-typed objects is viewed as an array, even though it might be represented as, for example, a hash table ๐๏ธ.
Representation of a Reply ๐
The data returned to the monitoring entity is a sequence of ASN.1 data items ๐ง. Each of these corresponds to one of the top-level dictionaries maintained by the monitored entity. The tags for these data items will be in the "application-specific" class. So if an entity has the above structure for its data, the only top-level data items that will be returned will have tags corresponding to these groups ๐ท๏ธ. If a query returned data from two of these, the representation might look like:
interfaces{ . . . } route-table{ . . . }
which is just a stream of two ASN.1 objects (each of which may consist of many sub-objects) ๐งต.
Data not in the root dictionary will have tags from the context-specific class. Therefore, data must always be fully qualified ๐ฏ. For example, the name of the entity would always be returned encapsulated inside an ASN.1 object for "system." If it weren't, there would be no way to tell if the object returned was "name" inside the "system" dictionary or "dest" inside the "interfaces" dictionary (assuming in this case that "name" and "dest" were assigned the same ASN.1 tag) ๐ต.
Having fully-qualified data simplifies decoding at the receiving end and allows the tags to be locally chosen. For example, definitions for tags dealing with ARP tables can't conflict with definitions for tags dealing with interfaces ๐งฉ. Therefore, the people doing the name assignments are less constrained ๐. In addition, most of the identifiers will be fairly small integers ๐ข.
It will often be the case that requested data may not be available. That can happen because the request was badly formed (asked for data that couldn't exist), or because the particular data item wasn't defined in a particular situation (time since last error, when there hasn't been an error) ๐ซ . In this situation, the returned data item will have the same tag as in the request, but will have zero-length data. Therefore, there can NEVER be an "undefined data" error ๐ฅ.
This allows completely generic queries to be composed without regard to whether the data is defined at all of the entities that will receive the request ๐. All of the available data will be returned, without generating errors that might otherwise terminate the processing of the query ๐ฅณ.
REPRESENTATION OF A REQUEST โจ
okay so a request to a monitored entity is ALSO just a sequence of ASN.1 data items ๐ and each one fits into one of these categories, bestie:
- Template ๐ธ these are objects with the same types as the objects that get returned by a request. the difference is that a template only describes the shape of the data, with no values in it. templates are how you pick out exactly which data you want back. the order you list things in a template doesn't decide the order of what comes back, and a template MAY be either simple or structured depending on what it's naming. the representations of the simple data items in a template all have a length of zero ๐
- Tag ๐ฆ a tag is a special case of a template that's a simple (non-structured) type. it names exactly one node in the dictionary tree, so it's giving precision.
- Opcodes ๐
these tell the query interpreter to actually DO something (we'll get into the details later). they're an application-specific type, and their value decides which operation runs. those values are defined in
RFC-1024. - Data ๐ง these are the same objects used to represent info coming back from an entity. sometimes you also have to send data as part of a request. like if you're asking about the interface with IP address
10.0.0.51, that address gets sent in the same format in the request as it would show up in a reply. ate
Data, Tags, and Templates are usually in the context-specific class, except for items in the root dictionary and a few special cases, which are in the application-specific class. ๐ซถ
QUERY LANGUAGE ๐
okay so queries are formed in a flexible way using what we call a "language" ๐ but it is lowkey not a programming language. there are operations that work on data, but most of the other features you'd find in a programming language are missing. specifically:
- programs are not stored in the query processor.
- the only form of temporary storage is a stack.
in the current version:
- there are no subroutines.
- there are no control structures defined in the language.
- there are no arithmetic or conditional operators.
these features could be added to the language if needed โจ
the whole goal here is a language that's expressive enough to write useful queries with, but still super simple to execute and to implement. simplicity is the whole vibe ๐
the stack is the main character here. it MAY contain templates (and therefore tags), data, or dictionaries (and therefore arrays) from the entity being monitored. at the start, it has exactly one item in it: the root dictionary ๐
here's how it goes. the query processor reads ASN.1 objects from the input stream. every object that isn't an opcode gets pushed onto the stack the second it's read. each opcode runs immediately, and it MAY remove things from the stack, and it MAY generate ASN.1 objects and send them to the output stream. portions of the response MAY be generated while the query is still being received, which is so iconic ๐ฅ
the list of opcodes below is provisional, so changes may need to be made to deal with additional needs ๐ง
in the descriptions, opcode names are in capital letters. each one is preceded by the arguments it pulls off the stack and followed by the results it leaves on the stack. for example:
OP a b OP t
that means the OP operator takes <a> and <b> off of the stack and leaves <t> on the stack. a lot of the operators leave the first operand (<a> in this example) on the stack for future use, which is giving reuse energy ๐ฆ
here are the operators defined in the query language ๐
GET ๐ธ
GET dict template GET dict
emit an ASN.1 object with the same "shape" as the given template. any items in the template that aren't in <dictionary> (or its components) come out as objects with a length of zero. that's how requests for data that isn't available get handled, whether it's because the data isn't defined or because it doesn't apply in this situation ๐
or ๐
or dict GET dict
if there is no template, you get all of the items in the dictionary. that's the same as providing a template that lists every single item in the dictionary ๐ซถ
BEGIN ๐ง
BEGIN dict1 tag BEGIN dict1 dict
pushes the value for dict{ tag } onto the stack, which SHOULD be another dictionary. at the same time, it produces the beginning octets of an ASN.1 object corresponding to that dictionary. it's up to the implementation to choose between using the "indefinite length" representation or going back and filling the length in later ๐
END ๐
END dict END --
pops the dictionary off of the stack and terminates the currently open ASN.1 object. it MUST be paired with a BEGIN โญ
Getting Items Based on Their Values
okay so there's one problem we kinda skipped over earlier ๐ when you're dealing with array data, how do you specify one or more entries based on some value sitting inside those entries? picture a situation with several interfaces, organized like this:
interfaces {
interface { type, ip-addr, in-pkts, out-pkts, ...}
interface { type, ip-addr, in-pkts, out-pkts, ...}
:
:
}
if you only want info about one interface (maybe because there's an enormous amount of data about each one), you still need a way to name it. one option is to just number the interfaces and refer to the one you want like this:
interfaces(3)
...which means the third one ๐ข
but this is probably not enough, since interface numbers may change over time, maybe from one reboot to the next. this method is totally not sufficient for arrays with lots of elements, like processes, routing tables, etc. honestly, large, changing arrays are probably the more common case in fact ๐ญ
because indexing isn't really useful in this context, there's no general mechanism for indexing in the language ๐
a better scheme is to select objects based on some value contained in them, like the IP address or the process name โจ that's what the GET-MATCH operator is for. it gives you this functionality in a fairly general way ๐
GET-MATCH ๐ฆ
GET-MATCH array value template GET-MATCH array
<array> SHOULD be an array (a dictionary containing only one type of item). the first tag in <value> and <template> MUST match this type. for each entry in <array>, match the <value> against the contents of that entry. if there is a match, emit the entry based upon <template>, just as in a GET operation ๐
if there are several leaf items in the value to be matched against, like this:
route-entry{ interface(1), cost(3) }
then all of them MUST match an array entry for it to be emitted ๐ฅ
here's how you'd use this operator to get the input and output packet counts for the interface with ip-address 10.0.0.51 ๐
interfaces BEGIN -- get dictionary
interface{ ip-addr(10.0.0.51) } -- value to match
interface{ in-pkts out-pkts } -- data template to get
GET-MATCH
END -- finished with dict
the exact meaning of a "match" depends on the characteristics of the entities being compared. in almost all cases it's a comparison for exact equality. BUT it is totally reasonable to define values that let matches do interesting things ๐ง for example, you could define three different flavors of ip-addr: one that has only the IP net number, one with the net+subnet, and one with the whole IP address. another option is to allow wildcards in IP addresses (e.g., if the "host" part of an IP address is all ones, then it would match against any IP address with the same net number) ๐
so, for all data items defined, the behavior of the match operation MUST be defined if it is not simple equality ๐ซถ
implementations don't have to provide the ability to use all items in an object to match against. it's expected that some data structures that are great for efficient lookup on one item may be super inefficient for matching against others. (for instance, routing tables are designed for lookup with IP addresses. it may be very difficult to search the routing table by matching against costs ๐)
NOTE: it would be desirable to provide a general-purpose filtering capability, rather than just "equality" as provided by GET-MATCH. however, because of the potential complexity of such a facility, the lack of a widely-accepted representation for filter expressions, and time pressure, we are not defining this mechanism now ๐ฅบ
however, if a generalized filtering mechanism is devised, the GET-MATCH operator will disappear ๐ธ
Data Attributes ๐
okay bestie, ASN.1 data is self-describing as far as the structure goes ๐
but it has zero clue what the data actually means. you can look at the raw bytes and figure out that an item is of type [context 5] and 4 octets long ๐ but that doesn't tell you if it's an integer, an IP address, or a 4-character string, or what it's an IP address of ๐
most of the time the answers come from RFC-1024 โจ, which defines all of the ASN.1 tags and their precise meaning. when extensions have been made, it MAY not be possible to get documentation on them ๐ญ (See the section about extensions, page 15.)
the query language has a set of operators that are parallel to GET and GET-MATCH ๐ฆ and they return a set of attributes describing the data. this information SHOULD be enough to let a human understand what the data means and let a sophisticated application treat it appropriately. it's giving enough to let an application format stuff on a display and decide whether or not to subtract one sample from another ๐ฅ
some of the attributes are textual descriptions that help a human get the nature of the data and give it meaningful labels ๐. extensive descriptions of standard data are OPTIONAL, since they're already defined in RFC-1024. complete descriptions of extensions MUST be available, even if they're documented in a user's manual. network firefighters ๐งฏ MAY not have the manual handy when the network is broken, so no excuses ๐ญ
the format of the attributes is not as simple as the format of the data itself ๐ง. you can't use the data's tag, since that would just look exactly like the data itself. the format is:
Attributes ::= [APPLICATION 2] 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,
}
for example, the attributes for
system{ name, clock-msec }
might be:
system{
Attributes{
tagASN1(name), valueFormat(IA5String),
longDesc("The name of the host"),
shortDesc("hostname")
},
Attributes{
tagASN1(clock-msec), valueFormat(Integer),
longDesc("milliseconds since boot"),
shortDesc("uptime"), unitsDesc("ms")
precision(4294967296),
properties(1)
}
note that in this example <name> and <clock-msec> are integer values for the ASN.1 tags for the two data items ๐. a complete definition of the contents of the Attributes type is in RFC-1024.
note that there will be exactly as many Attributes items in the result as there are objects in the template โจ. Attributes objects for items which do not exist in the entity will have a valueFormat of NULL, and none of the OPTIONAL elements will appear ๐ซถ.
GET-ATTRIBUTES ๐
GET-ATTRIBUTES
dict template GET-ATTRIBUTES dict
emit ASN.1 Attributes objects for the objects named in <template>. any items in the template that are not in <dictionary> (or its components) elicit an Attributes object with no.
or dict GET-ATTRIBUTES dict
if there is no template, emit Attribute objects for all of the items in the dictionary. this is equivalent to providing a template that lists all of the items in the dictionary, so you can get a complete list of a dictionary's contents ๐
GET-ATTRIBUTES-MATCH ๐
GET-ATTRIBUTES-MATCH
dict value template GET-ATTRIBUTES-MATCH dict <array>
<array> SHOULD be an array (a dictionary containing only one type of item). the first tag in <value> and <template> MUST match this type. for each entry in <array>, match the <value> against the contents of the entry. if there is a match, emit the attributes based upon <template>, just as in a GET-ATTRIBUTES operation.
GET-ATTRIBUTES-MATCH is necessary because there will be situations where the contents of the elements of an array MAY differ, even though the array elements themselves are the same type. the most obvious example is when several network interfaces exist and they're different types, with different data collected for each type ๐
NOTE: The GET-ATTRIBUTES-MATCH operator will disappear if a generalized filtering mechanism is devised.
ADDITIONAL NOTE: a much cleaner method would be to store the attributes as sub-components of the data item of interest ๐ง for example, requesting:
system{ clock-msec() } GET
would normally just get the value of the data. asking for an additional layer down the tree would now get its attributes:
system{ clock-msec{ shortDesc, unitsDesc } GET
would get the named attributes. (the attributes would be named with application-specific tags.) unfortunately, ASN.1 doesn't provide an obvious notation to describe this type of organization, so we're stuck with the GET-ATTRIBUTES operator ๐ค. however, if this cleaner organization becomes possible, this decision MAY be re-thought ๐ฆ
Examining Memory ๐งธ
even with the ability to symbolically access all of this information in an entity, there will still be times when you have to get to very low levels and examine memory, like in remote debugging operations ๐. the building blocks we've covered so far can easily be extended to let memory be examined.
memory is modeled as an array, with an ASN.1 representation of OctetString. because there's a huge variety of addressing architectures out there, the conversion between the OctetString and "memory" is very machine-dependent ๐. the only simple case is for byte-addressed machines with 8 bits per byte.
each address space in an entity is represented by one dictionary. in a one-address-space situation, this dictionary will be at the top level. if each process has its own address space, then one "memory" dictionary MAY exist for each process ๐ง
the GET-RANGE operator is provided for the primary purpose of retrieving the contents of memory, but it can be used on any array. it's only useful in these other contexts if the array index is meaningful ๐
GET-RANGE array start length GET-RANGE dict
get <length> elements of <array> starting at <start>. <start> and <length> are both ASN.1 INTEGER type ๐ข
the returned data MAY not be <length> octets long, since it MAY take more than one octet to represent one memory location ๐
memory is special in that it will not automatically be returned as part of a request for an entire dictionary. for example, if memory is part of the "system" dictionary, then requesting:
system{}
will emit the entire contents of the system dictionary, but not the memory item ๐ซฃ
NOTE: The GET-RANGE operator MAY disappear if a generalized filtering mechanism is devised.
Controlling Things ๐๏ธ
all of the operators defined so far only let data in an entity be retrieved. by replacing the "template" arguments used in the GET operators with values, you can change data in the entity ๐
there are many control operations that don't correspond to simply changing the value of a piece of data, like bringing an interface "down" or "up" ๐. in these cases, a special data item associated with the component being controlled (e.g., each interface) would be defined. control operations then consist of "setting" this item to an appropriate command code ๐ฏ
SET dict value SET dict
set the value(s) of data in the entity to the value(s) given in <value>.
SET-MATCH array mvalue svalue SET-MATCH dict
<array> SHOULD be an array (a dictionary containing only one type of item). the first tag in <mvalue> and <svalue> MUST match this type. for each entry in <array>, match the <mvalue> against the contents of the entry. if there is a match, set value(s) in the entity to the value(s) in <svalue>, just as in SET.
CREATE array value SET dict
insert a new entry into <array>. depending upon the context, there MAY be severe restrictions about what constitutes a valid <value> ๐ง
DELETE array value SET dict
delete the entry(s) in <array> that have values that match <value> ๐๏ธ
if there are several leaf items in the matched value, as in
route-entry{ interface(1), cost(3) }
all of them MUST match an array entry for any values to be changed ๐ซถ
here is an example of how this operator would be used to shut down the interface with ip-address 10.0.0.51, changing its status to "down" ๐
interfaces BEGIN -- get dictionary
interface{ ip-addr(10.0.0.51) } -- value to match
interface{ status(down) } -- value to set
SET-MATCH
END -- finished with dict
delete the routing table entry for 36.0.0.0 ๐งน
route-table BEGIN -- get dictionary
route-entry{ ip-addr(36.0.0.0) } -- value to match
DELETE
END -- finished with dict
note that this BEGIN/END pair ends up sending an empty ASN.1 item. we don't regard this as a problem, as it is likely that there will be some get operations executed in the same context. in addition, the "open" ASN.1 item provides the correct context for reporting errors. (See page 14.) ๐
NOTE: The SET-MATCH operator will disappear and the DELETE operator will change if a generalized filtering mechanism is devised.
Atomic Operations โ๏ธ
atomic operations can be provided if you want them, by allowing the stack to contain a fragment of a query. a new operation would take a query fragment, verify that it's executable, and execute it, atomically ๐งฉ
this is mentioned as a possibility, but it MAY be difficult to implement. more study is needed ๐
ERRORS ๐จ
okay bestie, so if some info gets requested but isn't available (maybe it doesn't apply to this implementation, maybe nobody collects it, whatever) it can always come back as "no-value" by giving the ASN.1 length as 0. no drama, just an empty slot ๐ซถ
when something else goes wrong, like having improper arguments on the top of the stack or trying to run BEGIN when the tag doesn't point to a dictionary, an ERROR object gets emitted. โจ the contents of that object spell out exactly what went wrong, and RFC-1024 is where you'll find the details ๐
now here's the iconic part ๐ there might be several unterminated ASN.1 objects in progress when the error hits, and every single one has to get terminated. each unterminated object gets closed with a copy of the ERROR object. how that looks depends on the length encoding in use: you fill in the value for the length (definite length form) or you emit two zero octets (indefinite length form). after all the objects are closed, one final copy of the ERROR object gets emitted. this structure means the error gets noticed at every level of interpretation on the receiving end. no level gets to be like "huh, what error?" ๐
if there was an error before any ASN.1 objects were generated, the result is just:
error(details)
and if a couple of ASN.1 objects were left unterminated, it might look like this:
interfaces{
interface { name(...) type(...) error(details) }
error(details)
}
error{details}
EXTENDING THE SET OF VALUES ๐ง
there are two ways to extend the set of values the query language understands. ๐
the first is to register your data and its meaning and get an ASN.1 tag assigned for it. this is the preferred method, because then that data specification is available for everyone to use. it's giving community energy ๐
the second method is to use the VendorSpecific application type to "wrap" the vendor-specific data. whenever an implementation defines data that isn't in RFC-1024, the "VendorSpecific" tag should be used to label a dictionary holding that vendor-specific data. say a vendor had some data about interfaces that was too strange to get standard numbers assigned for. they could represent it like this instead:
interfaces {
interface {
in-pkts, out-pkts, ...
VendorSpecific { ephemeris, declination }
}
}
in this case, ephemeris and declination are two context-dependent tags the vendor assigned for its non-standard data. ๐ฆ
if the vendor-specific method is chosen, the private data MUST have descriptions available through the GET-ATTRIBUTES and GET-ATTRIBUTESMATCH operators. ๐จ this is non-negotiable, bestie. even with that descriptive ability, the preferred method is still to get standard numbers assigned if possible ๐ซถ
IMPLEMENTATION ๐ ๏ธ
ngl, it's not really the vibe of RFCs to define an implementation, but the authors feel some suggestions will help early implementors of the query language. this list isn't meant to be complete. it's just hints about how the authors imagine the query processor could be implemented efficiently โจ
- the stack is an abstraction. it should be implemented with pointers, not by copying dictionaries, etc. ๐ฅ
- an object-oriented approach should make the initial implementation fairly easy. changes to the "shape" of the data items (which will certainly happen early on) will also be easier to make. ๐ธ
- only a few "messages" need to be understood by objects. ๐
- most interesting objects are dictionaries, and each one can be implemented using pointers to the data and procedure "hooks" that perform specific operations such as GET, MATCH, SET, etc. ๐ง
- the hardest part is actually extracting data from existing TCP/IP implementations that weren't designed with detailed monitoring in mind. this should be less of a problem if a system is designed with easy monitoring as a goal. ๐ญ
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, and you need both ๐
IS 8824 -- Specification (meaning, notation)
IS 8825 -- Encoding Rules (representation)
they're available from:
Omnicom Inc.
115 Park St, S.E. (new address as of March, 1987)
Vienna, VA 22180
(703) 281-1135
๐ฆ where this page comes from
- ietf: rfc1023 1987-10
- ๐ girlified by claude-haiku-5-5 on : a language model's rewrite of the original, under its terms