# Chapter 4. Developing an SNMP Agent

This chapter explains how to develop an SNMP agent or SMUX subagent to manage a system or network resource. It includes the following sections:

- "Overview of the Development Process," describes the necessary steps in the development of an SNMP agent using the Agent Development Kit.

- "Adding Your Custom Code to Generated Files," describes the individual steps in writing access functions for your MIB.

- "Testing Your Custom Code," describes how to test your custom access functions code before linking with the SNMP agent.

- "Building the Agent Executable," describes the individual steps in building the agent executable.

- "Testing the Agent," describes the steps in testing the agent executable.

- "Using Multiple SNMP Agents," discusses how you can run your SNMP agent if the host has an existing SNMP agent.

## Overview of the Development Process

The Agent Development Kit lets you build an SNMP agent to support manageable features of a system or network resource defined in your own MIB (private or not private). The steps involved in developing an SNMP agent or SMUX subagent, using the Agent Development Kit, follow:

1. Research and conceptualize, or form a model of, the system or network resource to be managed. You will want to select, or pinpoint, those features of the resource that are pertinent to system and network management goals.

2. Define the MIB for the objects to be managed.

   This step involves assigning variable names to each distinct manageable item, and determining and assigning the ASN.1 data types. The MIB must be written in ASN.1 notation. Refer to RFC 1212 (Concise MIB Definitions) for information about the syntax of defining MIB groups. Before defining a MIB, you may wish to obtain your own enterprise object identifier (OID). Refer to Appendix A, "SNMP Information," in the Agent Integrator Reference Manual for instructions on how to obtain an enterprise OID.

   One approach to writing a MIB is to start with an existing MIB, selecting features similar to those you want, and modifying the entries appropriately. Accordingly, you might want to look at some examples, such as standard MIBs or the BEA MIBs in the `bea.asn1` file. You must define your MIB in the file `my.asn1`. You can edit this file with any text editor, such as vi. The path name of the file is:

       $RELEASE_ROOT/agent_src/agentlib/my.asn1

   The Agent Development Kit code generator (`imibgenall`) checks for correct ASN.1 syntax and generates appropriate messages if it encounters syntax errors.

3. The application or component being managed requires instrumentation that provides information in a form that can be accessed by the agent or subagent.

   The information must be provided in a way that matches the definitions in the MIB. For example, a queueing system might contain a counter that collects data on the number of current entries in a queue. An application programming interface (API) function call might provide this information in the form of an integer value.

4. Define your environment (if you have not yet done so)

   The following is an example of how to define the working environment on a UNIX platform, using C shell commands:

       setenv RELEASE_ROOT install_dir
       set path = ($RELEASE_ROOT/bin $path)

   The following is an example of how to define the working environment on a Windows NT system:

       set RELEASE_ROOT = install_dir
       set path = %RELEASE_ROOT%\bin;%PATH%

   All other necessary environment settings are set automatically.

5. Run the MIB compiler/code generator (`imibgenall`) on a selected table or MIB group in your MIB file.

   The MIB compiler generates function skeletons to help you build your SNMP agent. The compiler also creates other source files, which you do not need to modify, which provide "hooks" to the access functions. The compiler also updates the makefiles that are needed to build the agent. To do this you use the compiler command:

   `imibgenall` `MIBRootName`

   `MIBRootName` designates the target MIB component. (Refer to Chapter 3, "Tools and Functions," for the complete syntax of this command.)

   The target MIB component (`MIBRootName`) must not be a group of groups but either a table or, for scalar objects, their ancestor group (i.e., the group that contains the scalar objects). If you have multiple groups of scalar objects or both scalar objects and tables, you will need to run the compiler multiple times. You run the compiler once for each group that contains scalar objects and once for each table. The MIB compiler expects to find the MIB definitions in the file `my.asn1`.

   Figure 4-1 shows an example of a private MIB. In this example, the objects contained in the groups `beaSecParam` and `beaSecStats` (objects `a` through `g`) are scalar objects whereas `beaSecMgrTable` and `beaSecUserTable` are tabular objects in the group `beaSec`. Objects `h` through `n` are columnar objects (i.e., objects that can have multiple scalar-valued instances).

   > Figure 4-1 Sample Private MIB Tree

   The `imibgenall` utility must be run four times to create four agent hook files, their corresponding access function files, and other files. This could be accomplished by entering the following four commands:

       imibgenall beaSecParam
       imibgenall beaSecStats
       imibgenall beaSecMgrTable
       imibgenall beaSecUserTable

6. Flesh out the generated function skeletons by adding code to access the data in the component or application being managed.

   To do this you edit the source file `MIBRootName``.c` generated by the MIB compiler. To flesh out the generated function skeletons, you use the instrumentation in the managed resource (referred to in step 3) to access the manageable features of the resource, to respond to SNMP GET and SET commands. Be sure that you create the file skeletons in the correct Agent Development Kit directory: `$RELEASE_ROOT/agent_src/agentlib`.

   The specific work required to carry out this step is described in "Adding Your Custom Code to Generated Files." The function skeletons and related components in the generated source files are described in Chapter 5, "Generated Access Function Templates."

7. Do stand-alone testing of the access functions.

   Once you have written the access functions (step 5), you can test them in stand-alone fashion, before linking with the rest of the agent code. To do this, compile the source file with the following command:

   `cd $RELEASE_ROOT/agent_src
   build_agent -r` `MIBRootName`

   This builds an executable named `csnmp_``MIBRootName`. This executable invokes the access functions and prints the values that are retrieved. To run this executable, enter the following command:

   `cd $RELEASE_ROOT/agent_src/agentlib
   ./csnmp_``MIBRootName`

8. Build the SNMP agent executable.

   Use the following command to build the agent:

   `build_agent -n` `YourSNMPAgent`

   The `build_agent` command is interactive and prompts you for the following information:

   - The MIB groups (corresponding to the files created in steps 4 through 5) to be included in the agent

   - The name of any other source code files

     Note: Any C files that you list here must be located in the following directory:

     `install_directory``/agent_src/agentlib`

   - The name of any program-specific precompiled libraries

     Note: You must use the absolute path to any libraries specified here.

   - Any program-specific compilation flags

9. Test the SNMP agent using the SNMP test utilities.

   The `snmpwalk` and `snmptest` utilities can be used to test the agent, as described in "Testing the Agent."

10. Integrate your private MIB with an SNMP management platform and test the agent with the management system.

    You need to follow the vendor instructions of your SNMP manager to integrate a new MIB into the manager.

Figure 4-2 illustrates the three basic phases in the development process.

> Figure 4-2 Writing, Building and Testing an Agent

Figure 4-3 illustrates the detailed steps of the agent development process.

> Figure 4-3 Agent Creation Flowchart

## Adding Your Custom Code to Generated Files

You create an access functions skeleton file and header file for each MIB group or table by running the MIB compiler/code generator (`imibgenall`). You need to run the compiler once for each group or table, by entering the command:

>     imibgenall MIBRootName

where `MIBRootName` is the name of a table or a group that contains scalar objects. The compiler expects to find your MIB definitions in the file `my.asn1`.

Be sure to generate the source files in the correct Agent Kit directory: `$RELEASE_ROOT/agent_src/agentlib`.

The generated files that need to be modified are:

`MIBRootName``.c` - generated access functions file

`MIBRootName``.h` - generated header file

The access function skeletons and other components of these generated files are described in Chapter 5, "Generated Access Function Templates."

Once you have generated these source files for a MIB group, you need to edit the files to carry out the following tasks:

1. Modify the constant definitions in the header file, if necessary.

The header file generated for each MIB group or table defines a constant that determines the maximum length for read-only and read/write string objects in the agent cache - `MAX_``ReadOnlyStringObjectName`. By default these values are set to 256. You may want to modify these values.

One of the constant values that gets defined for tabular MIB groups is `INIT_``MIBRootName``_ENTRIES`. By default, the value of this constant is 10. This constant represents the number of the entries in the cache for a table, space for which is allocated at initialization time. Another constant, `DELTA_``MIBRootName``_ENTRIES`, represents the number of new row entries for which space will be allocated if the currently allocated space for the cache table runs short.

You should set the `INIT_``MIBRootName``_ENTRIES` constant to the typical value of the number of entries in the table. For example, if you are writing a MIB for a process table, a good value of `INIT_``MIBRootName``_ENTRIES` can be 200 and if you are writing a MIB for disks, a good value of `INIT_``MIBRootName``_ENTRIES` would be 10. If you choose a big number for this constant, you will end up wasting memory. If you choose a small number, the SNMP agent may end up doing multiple allocations, which may fragment memory.

2. Use resource instrumentation in the refresh function to update the agent cache.

   When the agent receives an SNMP GET or GETNEXT request from a management station, it calls the generated `get_``ObjectName` functions to send back the current value of the object. The generated access functions file contains one such `get_``ObjectName` function for each object in the MIB group or table. These `get_``ObjectName` functions do not access the managed resource but retrieve the current value from the agent cache. Normally, you do not need to modify the `get_``ObjectName` functions.

   The agent tracks the age of the cache. Before calling the `get_``ObjectName` functions, the agent checks to determine whether the cache is older than the refresh rate. If it is older, the agent calls the function `refresh_``MIBRootName` before calling the `get_``ObjectName` functions for that MIB group. This refresh function updates the agent cache by directly accessing the managed resource - for example, using an API or system calls - to obtain current values of the attributes of the managed resource. Accordingly, you must edit the `refresh_``MIBRootName` function to add the code that directly accesses the managed resource and updates the object values in the agent cache.

   For example, if your MIB group has a scalar object `myQEntryCount` under the `myQ` MIB group, there will be a line in the generated code such as the following:

   `v_myQ.val_myQEntryCount =` `dummy_value`;

   The object values for a given MIB group are cached in the record`v_``MIBRootName` - in this example, `v_myQ`. This record is an instance of a structure of type `s_``MIBRootName`, defined in the header file. You will need to replace `dummy_value` with the code that provides the actual value accessed from the managed resource - in this example, this might involve a call to the queue system's API.

   For tabular objects, there can be zero or more rows (entries) in the table. Thus, you need to use some sort of loop control to fill out the values corresponding to each row in the table. This loop control requires some index that indicates the number of rows in the table. The dummy loop control code generated in the access functions file for tables looks something like this:

   >     int row_num;
   >     for (i=0; i<2; i++)
   >     {/* This dummy code assumes 2 rows in the table */
   >     /* The following statement increments the size of the table, if required */
   >     if (!create_MIBRootName_entry(&row_num)) return SNMP_FAILURE;

   A function `create_``MIBRootName``_entry()` is generated for each tabular object. This function creates a row in a table. The function returns an index of the newly created row. You should use this index to fill out the values of the objects in this row in the cache table. You must call `create_``MIBRootName``_entry()` each time in the control loop to create a row. This function should not be called from anywhere else in the agent program. Otherwise, the results are undefined. Before `refresh_``MIBRootName``()` is called by the SNMP agent, earlier rows are discarded. You can fill in the rows in the cache in any order. The SNMP agent automatically arranges them in lexicographic order in replying to the management station, as required by the SNMP standard.

3. Modify the refresh rate, if necessary.

   The agent maintains an internal cache of the values of the managed objects for each of its MIB groups. The agent tracks the time since the last refresh of the cache. When the agent receives an SNMP GET or GETNEXT request from a management station, it takes the value from the cache if the cache is not older than the refresh rate, otherwise it invokes the refresh function to update the cache. The refresh function uses the instrumentation in the managed resource to update the agent cache that maintains current values for the managed objects accessed by the agent. The refresh rate is specified by the constant `MIBRootName``_refresh_rate`. By default this value is set to 10 seconds. This is set by the following line in the generated access functions file:

       int MIBRootName_refresh_rate = 10 /* seconds */;

   You may want to increase this value if the object values in the managed resource do not change very quickly. If the value of `MIBRootName``_refresh_rate` is set to zero, the refresh function is called every time an object is queried by a management station. The refresh function is not invoked if no requests are received by the agent for the MIB group objects.

4. Flesh out the initialization function for the MIB group.

   An initialization function skeleton `init_``MIBGroupName` is generated for each MIB group or table. This function is called once by the agent the first time any object in the MIB group is polled. You should fill in this function with any required initialization code. This function returns SNMP_SUCCESS if successful, and SNMP_FAILURE otherwise. If this function returns a failure, the agent or subagent always returns "NO SUCH NAME" for any object in this MIB group. It is recommended that your code print a reason for failure when failures occur.

5. Edit the test functions to check values in set requests to ensure that the value is valid.

   If an SNMP agent receives an SNMP SET request containing more than one variable, either all objects are set or none are set. This behavior is known as atomic set and is one of the requirements of the SNMP standard. Atomic sets are supported using a two-phase commit. In order to implement this, for each read-write object a `test` and a `set` function is generated as part of the access functions file. On receiving a SET request from the management station, the SNMP agent first invokes the test function corresponding to each object involved in the SET request. If all `test` functions return success, only then does the agent invoke the `set` functions corresponding to these objects.

   For each scalar or columnar object that is defined as read-write in the MIB, a `test_``ObjectName` function skeleton is generated in the access functions file. You must flesh out each of these functions with code that checks to determine whether the value in the SET request is valid for the target attribute in the managed resource. The test function should return success only if the requested set value is valid for the target.

6. Use appropriate access methods (e.g., APIs or system calls) in set functions to modify attributes of the managed resource.

   To support SNMP SET requests, a `set_``ObjectName` function is generated in the access functions file for each MIB object that is defined as read-write. You need to add the necessary code to directly access the managed resource, such as API function calls. See "Row Deletion" and "Row Addition" in "Additional Programming Guidelines" for supporting SET requests for addition or deletion of rows in tables.

### Additional Programming Guidelines

The following points may be useful for writing the access functions code. In the following sections, `MIBRootName` refers to the root name of the MIB tree for which the C code is generated using `imibgenall`.

#### Community

If the agent is working as an SNMP agent, access functions can access the SNMP community used by the management station (if needed). The community is made available as an `extern` character array `sid`. The integer `sidlen` contains the length in bytes.

#### Defining Keywords in beamgr.conf

The SNMP agent uses a configuration file, `beamgr.conf`. Each record in `beamgr.conf` is separated by a newline character. Each record represents a keyword-value pair. The SNMP agent uses this file for maintaining certain configuration parameters and MIB object values. As the implementor of a MIB, you can use the same file for defining your own keywords.

To facilitate the use of this file, two APIs, `csam_get_keyword()` and `csam_set_keyword()`, are provided. `csam_get_word()` fetches the value corresponding to a given keyword and `csam_set_keyword()` sets the value corresponding to a keyword in `beamgr.conf`.

For more details about these two APIs, please refer to Chapter 3, "Tools and Functions."

#### Sample Traps Program

The Agent Development Kit includes APIs for generating traps from your SNMP agent. The trap APIs are described in Chapter 3, "Tools and Functions." The directory `$RELEASE_ROOT/agentsrc/agentlib/` contains a sample program, `snmp_appl.c`, illustrating the use of the SNMP trap APIs. A sample makefile, `sample.mk`, is also provided in the same directory to compile the sample program.

#### Row Deletion

In order to support row deletion capability, you must define an integer-valued read-write columnar object, which represents the status of the row. This object minimally has two valid values representing `valid` and `invalid` status. When the management station sets the value of this object to `invalid`, the effect should be of invalidating the corresponding row from the table.

To help the implementation of row deletion effects, an API `delete_``MIBRootName``_entry()` is provided. This function deletes a row from cache maintained by SNMP agent for the `MIBRootName` MIB group.

It should be noted that `delete_``MIBRootName``_entry()` only deletes the row from cache and has no effect on the source of the MIB information (e.g., kernel memory). In order to remove a row from the actual place of MIB information, you should take specific action inside the SET function corresponding to this MIB object, which is generated as part of the access functions file.

The function `delete_``MIBRootName``_entry()` expects the row index to be deleted, as an argument. This row index is the one that is passed to the set routine. `delete_``MIBRootName``_entry()` returns `SNMP_SUCCESS`, if the row deletion is successful, else it returns `SNMP_FAILURE`.

Note: This function must be called only from a `set` routine generated as part of the access functions file. If it is called from any other place, results are undefined.

#### Row Addition

In order to create a new row, the management station issues a SNMP set request containing one or more variables that refers to an object instance not currently available in the SNMP agent.

In order to support row creation, two functions, `test_``MIBRootName``_create()` and `set_``MIBRootName``_row()`, are created as part of the access functions file.

These two functions are called when a SET`` request is received for a non-existent row. First, all values corresponding to different columnar objects are stored in the cache, then `test_``MIBRootName``_create()` is called to check if the values are acceptable. If it returns success, `set_``MIBRootName``_row()` is called. `set_``MIBRootName``_row()` should always return success for atomic sets to work correctly.

Both of these functions are passed the index of the newly created row in the cache.

If `test_``MIBRootName``_create()` returns failure, row creation is assumed to have failed and the newly created row is removed from the cache.

It should be noted that a `set` request from the management station may not contain all columnar objects. It is the responsibility of the `test_``MIBRootName``_create()` function to figure out if this set of columnar objects is sufficient to create a row. The objects that are received in the set request are indicated by an SNMP_VALID value of their corresponding status. In the process, programmers may assign some suitable default values to other columnar objects in `test_``MIBRootName``_create()` and `set_``MIBRootName``_row_create()`.

## Testing Your Custom Code

To test the access function file `MIBRootName.c`, compile it as stand-alone code to bypass the SNMP requests. To do this, enter the following command:

>     cd $RELEASE_ROOT/agent_src
>     build_agent -r MIBRootName

This generates the file:

>     $RELEASE_ROOT/agent_src/agentlib/csnmp_MIBRootName

To run the executable, enter:

>     cd $RELEASE_ROOT/agent_src/agentlib/
>     ./csnmp_MIBRootName

This executes the get functions and prints the retrieved values. This ensures that the access functions work as a stand-alone utility before linking it with the rest of the SNMP agent code.

## Building the Agent Executable

Follow these steps to build the agent source code:

1. Change to the directory containing the agent source code:

    cd $RELEASE_ROOT/agent_src

2. Execute the `build_agent` command:

    build_agent -n SNMPAgentName

The `build_agent` command prompts you for additional information:

- The MIB groups to be included in the newly created agent

- The name of any source code files other than `MIBRootName``.c`

- The name of any program-specific precompiled libraries. This would consist of a list of absolute paths to the libraries, with entries separated by a blank.

- Any program-specific compilation flags

The `build_agent` command creates the agent executable in the file `SNMPAgentName` in the following directory:

    $RELEASE_ROOT/agent_src/agentlib

3. This step applies to UNIX platforms only: if you want to run the agent executable as a stand-alone SNMP agent (`-s` option) after building the agent executable, do the following:

- Set the owner of the executable to `root`.

- Set the s-uid bit of the executable.

  For example:

      cd $RELEASE_ROOT/agent_src/agentlib
      chown root SNMPAgentName
      chmod u+s SNMPAgentName
      ls -l SNMPAgentName
      -rwsrwxr-x  1 root 499712 Jun 20 10:58 SNMPAgentName*

4. Add the following services to your `/etc/services` files or to the services file on your yp host, if you are running yellow pages.

snmp | 161/udp
snmp-trap | 162/udp

5. Run the agent:

       ./SNMPAgentName -s

   The above command starts `SNMPAgentName` as an SNMP agent. If you want to run SNMPAgentName as a SMUX subagent, do not enter the `-s` command-line option. But in that case, a SMUX master agent or Agent Integrator should already be running.

   The `beamgr.conf` file can be placed in the `/etc` directory, or you can set the BEA_SM_BEAMGR_CONF environment variable to point to this file so the agent can send a `coldstart` trap when it starts up. The default host is the local host.

   For more details about command-line options of newly created agents, please refer to Chapter 5, "Starting the Subagents," in the Agent Integrator Reference Manual.

### Object Identifier Lists

The `sorted_oid_list` and `ascii_oid_list` files are automatically generated whenever an SNMP agent is built using the `build_agent` utility. These files contain a sorted cross-reference mapping managed object MIB names to OIDs.

#### Sample sorted_oid_list

Listing 4-1 is an example of the file `sorted_oid_list`, containing the `beaEx` example MIB objects.

> Listing 4-1 sorted_oid_list

>     ..
>     beaTrapHost: 1.3.6.1.4.1.140.1.8
>     beaTrapCommunity: 1.3.6.1.4.1.140.1.9
>     beaEx: 1.3.6.1.4.1.140.100
>     beaExIntRO: 1.3.6.1.4.1.140.100.1
>     beaExIntRW: 1.3.6.1.4.1.140.100.2
>     beaExStrRO: 1.3.6.1.4.1.140.100.3
>     beaExStrRW: 1.3.6.1.4.1.140.100.4
>     beaUnix: 1.3.6.1.4.1.140.2
>     beaPsTable: 1.3.6.1.4.1.140.2.1
>     ..

#### Sample ascii_oid_list

Listing 4-2 is an example of the file `ascii_oid_list`, containing the `beaEx` example objects. In this example, the file contents look the same because object names and object identifiers are alphabetically and numerically in the same order.

> Listing 4-2 ascii_oid_list

>     ..
>     beaEmProcsEnvtVar: 1.3.6.1.4.1.140.4.3
>     beaEmShmAllocated: 1.3.6.1.4.1.140.4.4
>     beaEx: 1.3.6.1.4.1.140.100
>     beaExIntRO: 1.3.6.1.4.1.140.100.1
>     beaExIntRW: 1.3.6.1.4.1.140.100.2
>     beaExStrRO: 1.3.6.1.4.1.140.100.3
>     beaExStrRW: 1.3.6.1.4.1.140.100.4
>     ..

## Testing the Agent

To test the SNMP agent executable set the environment variable `BEA_SM_SNMP_MIBFILE` to:

>     $RELEASE_ROOT/agent_src/agentlib/mib.txt

This ensures that the SNMP utilities can properly access the objects you added to the MIB file as the `mib.txt` file contains your new managed object definitions.

Test the agent by using either the `snmpwalk` or `snmptest` utilities. To use the `snmpwalk` utility, enter (all on one line):

>     snmpwalk hostname public private.enterprises.bea.beaEx

where `hostname` is the name or Internet address (in dot-dot notation) of the node where the agent is running.

The following data is retrieved from the `snmpwalk` command:

>     Name: private.enterprises.bea.beaEx.beaExIntRO.0
>     INTEGER: 1
>     Name: private.enterprises.bea.beaEx.beaExIntRW.0
>     INTEGER: 2
>     Name: private.enterprises.bea.beaEx.beaExStrRO.0
>     OCTET STRING- (ascii): get_beaExStrRO
>     Name: private.enterprises.bea.beaEx.beaExStrRW.0
>     OCTET STRING- (ascii): get_beaExStrRW
>     End of MIB.

Note: `snmpwalk` and `snmptest` print the full English name of objects because they have access to the `mib.txt` file.

Refer to the Chapter 3, "Tools and Functions," for complete instructions.

### Installing the New Agent

To install the new SNMP agent, copy the SNMP agent executable to the host where you want to use it. Set its s-uid bit and ownership to `root`.

Install the `beamgr.conf` and `bea_snmpd.conf` files and the following network services:

snmp | 161/udp
snmp-trap | 162/udp

The default location for the agent executable`` is `/usr/sbin` on SVR4 systems. The default location for `beamgr.conf` and `bea_snmpd.conf` is `/etc` on UNIX platforms. On Windows NT systems, the configuration files are located in `C:\etc.`

#### Troubleshooting

There are several error messages you may encounter during the installation process:

``

  This message informs you that an SNMP agent is already running on that host. To correct this problem, terminate the conflicting agent.

``

  This message informs you that the user-id and the group-id of the agent are incorrect. To correct this problem, change the effective user-id of the agent to `root`.

``

  This message informs you that the SNMP services are not configured correctly. To correct this problem, make sure the following services are available:

  snmp | 161/udp
  snmp-trap | 162/udp

## MIB Modification

If you realize that you made a mistake in defining a new MIB after you have created the C code using the `imibgenall` utility, you can undo it with the following command:

>     imibgenall -u MIBRootName

For example:

>     imibgenall -u beaEx

will undo any beaEx MIB related sources/modifications (previously created using `imibgenall beaEx`).

You might want to do this for one of the following reasons:

- You want to add or delete some objects in the MIB group.

- You want to change the type of certain MIB objects in this group.

- You want to change the INDEX clause of the MIB group.

- You want to change the access level of certain MIB object (i.e., changing from read-only to read-write or vice-versa).

Perhaps you have written code in the access functions file already which you do not want to lose. In such a situation, follow these steps:

1. Save a copy of the access function file. For example, on UNIX platforms, enter the following commands:

    cp MIBRootName.c MIBRootName.c.sav
    cp MIBRootName.h MIBRootName.h.sav

The following would be the Windows NT commands:

    copy MIBRootName.c MIBRootName.c.sav
    copy MIBRootName.h MIBRootName.h.sav

2. Undo the earlier created sources corresponding to this MIB. To do so, type:

    imibgenall -u MIBRootName

3. Make any desired modifications in the `MIBRootName` MIB group definitions in the `my.asn1` file.

4. Create the C code for this MIB group again. To do so, execute this command:

       imibgenall MIBRootName

   Most of your changes made to `MIBRootName``.c.sav` should be usable as is in `MIBRootName``.c`

5. Delete `MIBRootName``.c.sav`.

## Using Multiple SNMP Agents

If a host already has an SNMP agent (for example, an SNMP agent supplied by the hardware manufacturer), there are two strategies for using an agent generated by the Agent Development Kit:

- An agent generated using the Agent Development Kit can run on a port other than the standard SNMP port (161). If an SNMP manager can monitor two different SNMP agents on the same node using two different ports, you can use a port other than 161 for the SNMP manager to communicate with your agent. You can set the port for your SNMP agent with this command:

    SNMPAgentName -s -p port_number

where `port_number` is the alternate port. Configure the SNMP manager to communicate with this agent on `port_number` according to the manufacturer's instructions.

- If your third-party SNMP manager cannot communicate with an SNMP agent on a port other than port 161, you can use the BEA Agent Integrator to listen on port 161, and handle manager communications with SNMP agents (and subagents). Refer to the NON_SMUX_PEER entry in Chapter 6, "Configuration Files," in the Agent Integrator Reference Manual.