Using Prolog in Windows NT Network Configuration

David Hovel

Microsoft Research

Microsoft Corporation



Abstract Microsoft's Windows NT operating system uses an embedded Prolog interpreter to configure its local and wide-area network systems. Interdependent software and hardware components are abstracted into a simplified object-oriented framework using declarative information provided by each component's installation script. This information, along with the Prolog algorithm, is consulted into the embedded interpreter, which is then queried to construct the most usable configuration. The algorithm which solves the plumbing problem is described, including its positive and negative constraints, Prolog database and efficiency considerations. The results of the query, stored into NT's configuration database, inform each component of its load order and binding targets. A description of theC++ wrapper class is given and portation considerations are discussed. The Small Prolog interpreter is briefly described.

1. Overview

1.1 Windows NT

Windows NT was built from the start as a highly extensible platform for both client and server networking. Fully two years before its initial release, developers at Microsoft were developing NT on NT systems which supported multiple network cards and protocol stacks.

1.2 Problems in Network Configuration

Historically, networking systems have commonly suffered from three wide-spread problems. First, there was no system-pervasive configuration managementsoftware which could be used as a foundation. In MS-DOS, for example, every installer was required to have its own parsers for CONFIG.SYS and AUTOEXEC.BAT. This often meant that trivial formatting changes or unusual data could cause product installations to fail in catastrophic ways. Even simple changes in the order of component installation could result in situations which the vendors software was not designed to handle.

Second, there was no standard repository for network configuration information. Even experienced systems administrators were often uncertain as to which text files were required by the network and which components utilized them.

Third, the network components themselves were interconnected through proprietary software interfaces. Hence, components built by separate vendors could not generally be connected or bound even if their roles in the network software ensemble were inherently compatible.

1.3 Configuring Networks on Windows NT

1.3.1 Network Enhancements in Windows NT

NT mandates use of the Network Driver Interface Specification (NDIS), and the Transport Driver Interface (TDI) which together provide standardized network component behavior.

Most drivers are reentrant.

All configuration information is stored in a common configuration database with data types.

All drivers are dynamically demand-loaded.

All driver interconnections and configuration parameters are based upon explicit textual information in the configuration database.

Driver load ordering and grouping is governed by explicit information in the configuration database.

The NT operating system in its entirety is functioning during installation, configuration and networking initiation, as opposed to a reduced boot-time load system of limited capability.

By comparison with other networking platforms, NT presents a highly normalized and well-behaved interface centered around its configuration database called the Registry. Given a correct Registry, the system runs correctly.

1.3.2 The Centralized Approach

In this architecture, the user would have the opportunity to rescind decisions made by the network configuration software, but neither the user nor vendor software could coerce the system into invalid configurations.

1.3.3 The Goals of Network Configuration

Support a diverse and rapidly evolving ensemble of software and hardware components.

Strive for a no-questions-asked network installation process.

Offload configuration intelligence from individual components to the generalized configuration software.

To achieve a high degree of user acceptance, the planned implementation approach was all-encompassing. In one pass, the network configuration software would be required to:

Detect a network card.

Install all the necessary software components.

Allow the user to install additional hardware and software components.

Create all the network configuration information.

Load and start the network.

Use the new systems network capabilities to allow the user to join an NT security domain.

Equally importantly, the system was supposed to do as little interaction with the user as possible. To the best of my knowledge, no previous networking system has ever been installed in this fashion before.

2. Plumbing the Network Lattice

2.1 The Network Lattice

Figure 1: An Example Network Lattice

The lattice is considered to be oriented from the user downwards; the uppermost nodes are the originators of network traffic and the lowest nodes are those connecting directly to the hardware.

2.2 Plumbing and the NT Namespace

In Windows NT, as in the members of the Unixi family, the concept of a file has been extended to include any data source or sink. As in Unix, NT maintains names for all such files in a global namespace organized as a tree, and they are accessed using the system openfunction in the same manner as disk files.

Therefore, each ordered pair previously mentioned is really a triple: the final element is the name of the file or port to be created and opened. This triple constitutes an instruction to the higher component to open a connection to the lower component using the given name, as well as an instruction to the lower component to provide an access port of the name required.

2.3 Load Ordering and Grouping

The solution to the plumbing problem is used to determine the loading sequence of the network modules. If component A must open a file or port created by component B, then B must complete its loading and initialization operations before A is loaded.

2.4 Decorating the Paths Through the Lattice

In this context, a path is defined as a complete routing from an initiating component downward through the lattice to a network adapter card or other end-point. Product loading requirements and ordering are determined by the topological relationships inherent in the complete set of all constructed paths.

2.5 Object-Oriented Configuration Design

In systems employing several protocol stacks, network cards and serial ports, components of a similar type are treated virtually identically. While the exact details of their addressing might vary, their participation in the network lattice is determined solely by the component type.

Elements of object-oriented design were adopted to exploit this type-versus-instance analogy. In this scheme, there is a global space of named interface classes. A class may be basic(that is, have no parent class), or it may inherit from one or more parent classes. A component type is defined by the class names describing its upper and lower boundaries. In addition, a class may be marked as a logical end-point, which means it acts as a terminal point for interconnections in the lattice. A network adapter card is always considered a logical end-point.

Instances of classes can be bound to one another in the sense that an interconnection can legitimately be made between them. There is a close analogy between this approach and class compatibility in common object-oriented programming languages. For example, if two classes aClass and bClass are declared in Prolog as bindable(aClass,bClass), then instances of aClass can be bound to instances of bClass, but not the reverse. In addition, instances of their subclasses can also be bound together, but not instances of their superclasses. In the case of multiple inheritance, instances of such classes inherit the bindablity of every superclass.

2.6 Object Model

In the configuration model, each network component is abstracted as a black box with an upper and a lower boundary or interface. Most types of objects have the same class interface at both boundaries. However, there are occasions on which it is necessary to consider the upper and lower layers of an object as having distinct properties.

Figure 2: Basic Component Object Model

The definition of a network component type, then, requires the declaration of the interface classes to which its upper and lower layers belong. It is important to remember that these semantic or behavioral interface classes are defined on top of the NDIS and TDI syntactic or general interface standards.

2.7 Overview of the Algorithm

The set of all interface class declarations.

The set of all component type declarations.

The set of all components which are actively present.

Rules governing interface binding.

Rules governing symbolic device name construction.

Given these rules, all dataflow paths can be determined, and the lattice can be constructed and decorated.

3.Choosing Prolog

3.1 Viewing the Problem as Declarative

3.2 Experience with Small Prolog

Another issue clearly in its favor is that the interpreteruses a simplified syntax similar to that of LISP; this format made SProlog clauses very easy to generate and process using recursive descent.

3.3 Prototyping

During the course of the project there were several occasions when the Prolog algorithm had to be enhanced. On each of these occasions, changes were developed and tested in console mode; when completed, the network configuration dynamic-link library was simply relinked with an updated Prolog text program. This ability to perform continual prototyping within a stable application was greatly beneficial.

3.4 Search Space Management

3.5 Introduction to SProlog

3.5.1 Origin

3.5.2 Syntax

/* membership in a list */

((member X (X|Y))

)

((member X (A|B))

(member X B)

)

3.5.3 Advantages

3.5.3.1 Size and Portability

Since M. de Feraudy had already considered some of the issues involved in embedding the interpreter, the outer query loop was structured so that it could be readily replaced.

3.5.3.2 Memory Usage

3.5.3.3 List and String Handling

3.5.4 Limitations

The absence of garbage collection was not a primary concern since the lattice generation algorithm ran only once during network reconfiguration. Being a virtual memory operating system, NT had no problems supporting the full contingent of memory required for the worst-case scenarios. Similarly, the absence of tail recursion elimination, while a minor performance consideration, did not affect run-time memory use or performance significantly.

4. The Lattice Generation Algorithm

4.1 Background

4.1.1 The Configuration Registry

4.1.2 Network Configuration Information

device-specific information; that is, information which is particular to the driver or software component in question;

declarative information used to support network lattice construction.

The latter type of information governs the behavior of the lattice construction algorithm. As will be explained later, much of it is converted to SProlog declarative facts or base clauses.

4.1.3 Steps in the Installation Process

Detect or choose a network card and run its installation script. Run the installation scripts for the default software components Allow the user to pick additional products to install (scripts to run) Gather the network-related Registry information and convert it to SProlog format. Run the binding algorithm to generate a correct lattice. Convert the query results to Registry format. Update the Registry to reflect the new lattice. Allow the user to rescind any bindings which are inappropriate or unnecessary. Perform a topological sort based upon the revised (active) lattice and update the Registry with the load order information. Start the network by loading the network redirector.

The steps 4, 5 and 6 above are those which involve the embedded Prolog engine and its interfaces. Given a correct lattice and software loading order, the last step suffices to cause the entire network ensemble to be loaded and initiated correctly.

4.2 Basic Algorithm

The algorithm then backtracks across every possible pair of device interfaces and determines if they are bindable. Those which succeed are asserted as facts into the interpreters database. The discovered set of possible bindings is then examined, and any bindings which violate constraints are retracted. (See the appendix Basic Binding Backtrack Algorithmfor more information.)

Finally, the set is converted into usable binding information; that is, the actual device names to be used in the NT namespace are constructed and topological information about the exact path through the network lattice is generated.

(Note: The full details of the generation of binding names is beyond the scope of this document.)

4.3 Constraints

Any potential path in the lattice which does not reach a network adapter card or a logical end-point is discarded as moot.

Bindings can be declared as exclusive, or restricted to a single instance. This can be applied to either the source or sink end of a binding or both.

or restricted to a single instance. This can be applied to either the source or sink end of a binding or both. If multiple conflicting binding rules apply, a simple priority scheme is used to resolve contention.

Another more general negative binding constraint can be specified that prevents any two device classes from appearing anywhere along a binding path in the lattice, regardless of the degree of separation.

Binding information which is found to violate a constraint or which loses a contention resolution arbitration is retracted.

4.4 Binding Names and Paths

Along with these names, the exact path of each binding down through the network lattice is preserved in a list of component tokens stored with each individual binding fact. This information is used by the calling program to perform a topological sort as part of the process of generating the correct network component loading sequence.

4.5 Converted Fact Predicates

4.5.1 devClass/3

This fact declares that a class exists, and declares its parent class or uses the token basic to indicate that it has no parent. A final token of yes or no indicates whether the class is a logical end-point or terminus for the binding search. Multiple devClass facts are used to indicate multiple inheritance..

4.5.2 devType/4

This fact declares that a product type exists, but may not necessarily be present or active. Each boundary (upper and lower) of the product can be declared to be of a different class. The Registry fact conversion software defaults both boundaries to be the same if only one is specified.

4.5.3 present/4

This fact declares that an instance of a product type exists and tells where it is located in the Registry.

4.5.4 bindable/5

This fact declares that instances of fromClass can bind to instances of toClass. The Boolean exclusive tokens are used to indicate whether the upper or lower layer can only be bound multiply or only once.

The bindValue token is a number from 0 to 100. Multiple applicable binding rules are resolved as follows. The rule with the highest binding value is used in preference to all others. Then, the rule closest to the binder's true class is used in preference to rules based on parent classes. Finally, multiple specific rules are order-dependent; the first found is the one used.

4.5.5 devBind/5

This fact is used to control the generation of names in the NT namespace. The arguments indicate the following:

objectName: This is a string value used as the basis for names generated for this product (contrasted to the tokenized name used inside the interpreter).

getsBindings: This Boolean value determines whether or not a product's Registry area is actually updated with binding information.

appearsInBindings: This Boolean value determines whether or not a product's name is inserted into the generated name. Some layers of a protocol ensemble can be "invisible".

namingMethod: The NT driver architecture allows drivers to create containers (or directories) in the NT name space; if a driver supports this, backslashes are used to separate product level names; otherwise, they are simply concatenated using underscores to create unique names.

4.5.6 block/2

This fact indicates a constraint which prevents any path from being created which connects, however distantly, an instance of fromClass to an instance of toClass. Normal rules of inheritance apply. Note that this is an ordered pair and therefore would not in and of itself prevent a binding in the opposite direction.

4.6 Using the Facts: A Simple Binding Example

Figure 3: Binding Two Components

The arc connecting redir to proto1 would be instantiated by the binding algorithm because backtracking across all possible component pairs would present the list [redir proto1], and backtracking across all bindable facts would yield the rule which directly permitted binding between the lower interface of redir and the upper interface of proto1.