/*
 * Note: this file originally auto-generated by mib2c using
 *       version : 1.32 $ of : mfd-top.m2c,v $ 
 *
 * $Id: ipAddressTable.c,v 1.12 2004/10/19 21:04:24 rstory Exp $
 */
/** \mainpage MFD helper for ipAddressTable
 *
 * \section intro Introduction
 * Introductory text.
 *
 */
/*
 * standard Net-SNMP includes 
 */
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>

#include <net-snmp/data_access/interface.h>

/*
 * include our parent header 
 */
#include "ipAddressTable.h"

#include <net-snmp/agent/mib_modules.h>

#include "ipAddressTable_interface.h"

oid             ipAddressTable_oid[] = { IPADDRESSTABLE_OID };
int             ipAddressTable_oid_size = OID_LENGTH(ipAddressTable_oid);

void            initialize_table_ipAddressTable(void);


/**
 * Initializes the ipAddressTable module
 */
void
init_ipAddressTable(void)
{
    DEBUGMSGTL(("verbose:ipAddressTable:init_ipAddressTable", "called\n"));

    /*
     * TODO:300:o: Perform ipAddressTable one-time module initialization.
     */

    /*
     * here we initialize all the tables we're planning on supporting
     */
    if (should_init("ipAddressTable"))
        initialize_table_ipAddressTable();

}                               /* init_ipAddressTable */

/**
 * Initialize the table ipAddressTable 
 *    (Define its contents and how it's structured)
 */
void
initialize_table_ipAddressTable(void)
{
    ipAddressTable_registration_ptr user_context;
    u_long          flags;

    DEBUGMSGTL(("verbose:ipAddressTable:initialize_table_ipAddressTable",
                "called\n"));

    /*
     * TODO:301:o: Perform ipAddressTable one-time table initialization.
     */

    /*
     * TODO:302:o: |->Initialize ipAddressTable user context
     * if you'd like to pass in a pointer to some data for this
     * table, allocate or set it up here.
     */
    user_context = NULL;

    /*
     * No support for any flags yet, but in the future you would
     * set any flags here.
     */
    flags = 0;

    /*
     * call interface initialization code
     */
    _ipAddressTable_initialize_interface(user_context, flags);
}                               /* initialize_table_ipAddressTable */

/**
 * pre-request callback
 *
 *
 * @retval MFD_SUCCESS              : success.
 * @retval MFD_ERROR                : other error
 */
int
ipAddressTable_pre_request(ipAddressTable_registration_ptr user_context)
{
    DEBUGMSGTL(("verbose:ipAddressTable:ipAddressTable_pre_request",
                "called\n"));

    /*
     * TODO:510:o: Perform ipAddressTable pre-request actions.
     */

    return MFD_SUCCESS;
}                               /* ipAddressTable_pre_request */

/**
 * post-request callback
 *
 *
 * @retval MFD_SUCCESS : success.
 * @retval MFD_ERROR   : other error (ignored)
 */
int
ipAddressTable_post_request(ipAddressTable_registration_ptr user_context)
{
    DEBUGMSGTL(("verbose:ipAddressTable:ipAddressTable_post_request",
                "called\n"));

    /*
     * TODO:511:o: Perform ipAddressTable pos-request actions.
     */

    return MFD_SUCCESS;
}                               /* ipAddressTable_post_request */


/**********************************************************************
 **********************************************************************
 ***
 *** Table ipAddressTable
 ***
 **********************************************************************
 **********************************************************************/
/*
 * ipAddressTable is subid 34 of ip.
 * Its status is Current.
 * OID: .1.3.6.1.2.1.4.34, length: 8
 */

/*
 * ---------------------------------------------------------------------
 * * TODO:200:r: Implement ipAddressTable data context functions.
 */
/*
 * ipAddressTable_allocate_data
 *
 * Purpose: create new ipAddressTable_data.
 */
ipAddressTable_data *
ipAddressTable_allocate_data(void)
{
    /*
     * TODO:201:r: |-> allocate memory for the ipAddressTable data context.
     */
    ipAddressTable_data *rtn = netsnmp_access_ipaddress_entry_create();

    DEBUGMSGTL(("verbose:ipAddressTable:ipAddressTable_allocate_data",
                "called\n"));

    if (NULL == rtn) {
        snmp_log(LOG_ERR, "unable to malloc memory for new "
                 "ipAddressTable_data.\n");
    }

    return rtn;
}                               /* ipAddressTable_allocate_data */

/*
 * ipAddressTable_release_data
 *
 * Purpose: release ipAddressTable data.
 */
void
ipAddressTable_release_data(ipAddressTable_data * data)
{
    DEBUGMSGTL(("verbose:ipAddressTable:ipAddressTable_release_data",
                "called\n"));

    /*
     * TODO:202:r: |-> release memory for the ipAddressTable data context.
     */
    netsnmp_access_ipaddress_entry_free(data);
}                               /* ipAddressTable_release_data */


/*---------------------------------------------------------------------
 * IP-MIB::ipAddressEntry.ipAddressAddrType
 * ipAddressAddrType is subid 1 of ipAddressEntry.
 * Its status is Current, and its access level is NoAccess.
 * OID: .1.3.6.1.2.1.4.34.1.1
 * Description:
The address type of ipAddressAddr.
 *
 * Attributes:
 *   accessible 0     isscalar 0     enums  1      hasdefval 0
 *   readable   0     iscolumn 1     ranges 0      hashint   0
 *   settable   0
 *
 * Enum range: 5/8. Values:  unknown(0), ipv4(1), ipv6(2), ipv4z(3), ipv6z(4), dns(16)
 *
 * Its syntax is InetAddressType (based on perltype INTEGER)
 * The net-snmp type is ASN_INTEGER. The C type decl is long (u_long)
 *
 *
 *
 * NOTE: NODE ipAddressAddrType IS NOT ACCESSIBLE
 *
 *
 */
/**
 * map a value from its original native format to the MIB format.
 *
 * @retval MFD_SUCCESS         : success
 * @retval MFD_ERROR           : Any other error
 *
 * @note parameters follow the memset convention (dest, src).
 *
 * @note generation and use of this function can be turned off by re-running
 * mib2c after adding the following line to the file
 * default-node-ipAddressAddrType.m2d :
 *   @eval $m2c_node_skip_mapping = 1@
 *
 * @remark
 *  If the values for your data type don't exactly match the
 *  possible values defined by the mib, you should map them here.
 *  Otherwise, just do a direct copy.
 */
int
ipAddressAddrType_map(u_long * mib_ipAddressAddrType_val_ptr,
                      u_long raw_ipAddressAddrType_val)
{
    netsnmp_assert(NULL != mib_ipAddressAddrType_val_ptr);

    DEBUGMSGTL(("verbose:ipAddressTable:ipAddressAddrType_map",
                "called\n"));

    /*
     * TODO:241:o: |-> Implement ipAddressAddrType enum mapping.
     * uses INTERNAL_* macros defined in the header files
     */
    switch (raw_ipAddressAddrType_val) {
    case INTERNAL_IPADDRESSADDRTYPE_IPV4:
        *mib_ipAddressAddrType_val_ptr = INETADDRESSTYPE_IPV4;
        break;

    case INTERNAL_IPADDRESSADDRTYPE_IPV6:
        *mib_ipAddressAddrType_val_ptr = INETADDRESSTYPE_IPV6;
        break;

    default:
        snmp_log(LOG_ERR, "couldn't map value %d for ipAddressAddrType\n",
                 raw_ipAddressAddrType_val);
        *mib_ipAddressAddrType_val_ptr = INETADDRESSTYPE_UNKNOWN;
    }

    return MFD_SUCCESS;
}                               /* ipAddressAddrType_map */


/**
 * set mib index(es)
 *
 * @param tbl_idx mib index structure
 *
 * @retval MFD_SUCCESS     : success.
 * @retval MFD_ERROR       : other error.
 *
 * @remark
 *  This convenience function is useful for setting all the MIB index
 *  components with a single function call. It is assume that the C values
 *  have already been mapped from their native/rawformat to the MIB format.
 */
int
ipAddressTable_indexes_set_tbl_idx(ipAddressTable_mib_index * tbl_idx,
                                   u_long ipAddressAddrType_val,
                                   char *ipAddressAddr_val_ptr,
                                   size_t ipAddressAddr_val_ptr_len)
{
    DEBUGMSGTL(("verbose:ipAddressTable:ipAddressTable_indexes_set_tbl_idx", "called\n"));

    /*
     * ipAddressAddrType(1)/InetAddressType/ASN_INTEGER/long(u_long)//l/a/w/E/r/d/h 
     */
    /** WARNING: this code might not work for netsnmp_ipaddress_entry */
    ipAddressAddrType_map(&tbl_idx->ipAddressAddrType,
                          ipAddressAddrType_val);

    /*
     * ipAddressAddr(2)/InetAddress/ASN_OCTET_STR/char(char)//L/a/w/e/R/d/h 
     */
    tbl_idx->ipAddressAddr_len = sizeof(tbl_idx->ipAddressAddr);
    /** WARNING: this code might not work for netsnmp_ipaddress_entry */
    /*
     * make sure there is enough space for ipAddressAddr data
     */
    if (tbl_idx->ipAddressAddr_len < ipAddressAddr_val_ptr_len) {
        snmp_log(LOG_ERR, "not enough space for value\n");
        return MFD_ERROR;
    }
    tbl_idx->ipAddressAddr_len = ipAddressAddr_val_ptr_len;
    memcpy(tbl_idx->ipAddressAddr, ipAddressAddr_val_ptr,
           tbl_idx->ipAddressAddr_len * sizeof(tbl_idx->ipAddressAddr[0]));


    return MFD_SUCCESS;
}                               /* ipAddressTable_indexes_set_tbl_idx */

/**
 * @internal
 * set row context indexes
 *
 * @param reqreq_ctx the row context that needs updated indexes
 *
 * @retval MFD_SUCCESS     : success.
 * @retval MFD_ERROR       : other error.
 *
 * @remark
 *  This function sets the mib indexs, then updates the oid indexs
 *  from the mib index.
 */
int
ipAddressTable_indexes_set(ipAddressTable_rowreq_ctx * rowreq_ctx,
                           u_long ipAddressAddrType_val,
                           char *ipAddressAddr_val_ptr,
                           size_t ipAddressAddr_val_ptr_len)
{
    DEBUGMSGTL(("verbose:ipAddressTable:ipAddressTable_indexes_set",
                "called\n"));

    if (MFD_SUCCESS !=
        ipAddressTable_indexes_set_tbl_idx(&rowreq_ctx->tbl_idx,
                                           ipAddressAddrType_val,
                                           ipAddressAddr_val_ptr,
                                           ipAddressAddr_val_ptr_len))
        return MFD_ERROR;

    /*
     * convert mib index to oid index
     */
    rowreq_ctx->oid_idx.len = sizeof(rowreq_ctx->oid_tmp) / sizeof(oid);
    if (0 != ipAddressTable_index_to_oid(&rowreq_ctx->oid_idx,
                                         &rowreq_ctx->tbl_idx)) {
        return MFD_ERROR;
    }

    return MFD_SUCCESS;
}                               /* ipAddressTable_indexes_set */


/*---------------------------------------------------------------------
 * IP-MIB::ipAddressEntry.ipAddressIfIndex
 * ipAddressIfIndex is subid 3 of ipAddressEntry.
 * Its status is Current, and its access level is Create.
 * OID: .1.3.6.1.2.1.4.34.1.3
 * Description:
The index value which uniquely identifies the interface to
            which this entry is applicable.  The interface identified by
            a particular value of this index is the same interface as
            identified by the same value of the IF-MIB's ifIndex.
 *
 * Attributes:
 *   accessible 1     isscalar 0     enums  0      hasdefval 0
 *   readable   1     iscolumn 1     ranges 1      hashint   1
 *   settable   1
 *   hint: d
 *
 * Ranges:  1 - 2147483647;
 *
 * Its syntax is InterfaceIndex (based on perltype INTEGER32)
 * The net-snmp type is ASN_INTEGER. The C type decl is long (long)
 */
/**
 * Extract the current value of the ipAddressIfIndex data.
 *
 * Set a value using the data context for the row.
 *
 * @param rowreq_ctx
 *        Pointer to the row request context.
 * @param ipAddressIfIndex_val_ptr
 *        Pointer to storage for a long variable
 *
 * @retval MFD_SUCCESS         : success
 * @retval MFD_SKIP            : skip this node (no value for now)
 * @retval MFD_ERROR           : Any other error
 */
int
ipAddressIfIndex_get(ipAddressTable_rowreq_ctx * rowreq_ctx,
                     long *ipAddressIfIndex_val_ptr)
{
   /** we should have a non-NULL pointer */
    netsnmp_assert(NULL != ipAddressIfIndex_val_ptr);


    DEBUGMSGTL(("verbose:ipAddressTable:ipAddressIfIndex_get",
                "called\n"));

    netsnmp_assert(NULL != rowreq_ctx);

    /*
     * TODO:231:o: |-> Extract the current value of the ipAddressIfIndex data.
     * set (* ipAddressIfIndex_val_ptr ) from rowreq_ctx->data
     */
    (*ipAddressIfIndex_val_ptr) = rowreq_ctx->data->if_index;

    return MFD_SUCCESS;
}                               /* ipAddressIfIndex_get */

/*---------------------------------------------------------------------
 * IP-MIB::ipAddressEntry.ipAddressType
 * ipAddressType is subid 4 of ipAddressEntry.
 * Its status is Current, and its access level is Create.
 * OID: .1.3.6.1.2.1.4.34.1.4
 * Description:
The type of address.  broadcast(3) is not a valid value for
            IPv6 addresses (RFC3513).  
 *
 * Attributes:
 *   accessible 1     isscalar 0     enums  1      hasdefval 1
 *   readable   1     iscolumn 1     ranges 0      hashint   0
 *   settable   1
 *   defval: unicast
 *
 * Enum range: 2/8. Values:  unicast(1), anycast(2), broadcast(3)
 *
 * Its syntax is INTEGER (based on perltype INTEGER)
 * The net-snmp type is ASN_INTEGER. The C type decl is long (u_long)
 */
/**
 * Extract the current value of the ipAddressType data.
 *
 * Set a value using the data context for the row.
 *
 * @param rowreq_ctx
 *        Pointer to the row request context.
 * @param ipAddressType_val_ptr
 *        Pointer to storage for a long variable
 *
 * @retval MFD_SUCCESS         : success
 * @retval MFD_SKIP            : skip this node (no value for now)
 * @retval MFD_ERROR           : Any other error
 */
int
ipAddressType_get(ipAddressTable_rowreq_ctx * rowreq_ctx,
                  u_long * ipAddressType_val_ptr)
{
   /** we should have a non-NULL pointer */
    netsnmp_assert(NULL != ipAddressType_val_ptr);


    DEBUGMSGTL(("verbose:ipAddressTable:ipAddressType_get", "called\n"));

    netsnmp_assert(NULL != rowreq_ctx);

    /*
     * TODO:231:o: |-> Extract the current value of the ipAddressType data.
     * set (* ipAddressType_val_ptr ) from rowreq_ctx->data
     */
    (*ipAddressType_val_ptr) = rowreq_ctx->data->ia_type;

    return MFD_SUCCESS;
}                               /* ipAddressType_get */

/*---------------------------------------------------------------------
 * IP-MIB::ipAddressEntry.ipAddressPrefix
 * ipAddressPrefix is subid 5 of ipAddressEntry.
 * Its status is Current, and its access level is ReadOnly.
 * OID: .1.3.6.1.2.1.4.34.1.5
 * Description:
A pointer to the row in the prefix table to which this
            address belongs.  May be { 0 0 } if there is no such row.
 *
 * Attributes:
 *   accessible 1     isscalar 0     enums  0      hasdefval 1
 *   readable   1     iscolumn 1     ranges 0      hashint   0
 *   settable   0
 *   defval: zeroDotZero
 *
 *
 * Its syntax is RowPointer (based on perltype OBJECTID)
 * The net-snmp type is ASN_OBJECT_ID. The C type decl is oid (oid)
 * This data type requires a length.  (Max 2147483647)
 */
/**
 * Extract the current value of the ipAddressPrefix data.
 *
 * Set a value using the data context for the row.
 *
 * @param rowreq_ctx
 *        Pointer to the row request context.
 * @param ipAddressPrefix_val_ptr_ptr
 *        Pointer to storage for a oid variable
 * @param ipAddressPrefix_val_ptr_len_ptr
 *        Pointer to a size_t. On entry, it will contain the size (in bytes)
 *        pointed to by ipAddressPrefix.
 *        On exit, this value should contain the data size (in bytes).
 *
 * @retval MFD_SUCCESS         : success
 * @retval MFD_SKIP            : skip this node (no value for now)
 * @retval MFD_ERROR           : Any other error
*
 * @note If you need more than (*ipAddressPrefix_val_ptr_len_ptr) bytes of memory,
 *       allocate it using malloc() and update ipAddressPrefix_val_ptr_ptr.
 *       <b>DO NOT</b> free the previous pointer.
 *       The MFD helper will release the memory you allocate.
 *
 * @remark If you call this function yourself, you are responsible
 *         for checking if the pointer changed, and freeing any
 *         previously allocated memory. (Not necessary if you pass
 *         in a pointer to static memory, obviously.)
 */
int
ipAddressPrefix_get(ipAddressTable_rowreq_ctx * rowreq_ctx,
                    oid ** ipAddressPrefix_val_ptr_ptr,
                    size_t *ipAddressPrefix_val_ptr_len_ptr)
{
    oid            *src;
    int             len;

   /** we should have a non-NULL pointer and enough storage */
    netsnmp_assert((NULL != ipAddressPrefix_val_ptr_ptr)
                   && (NULL != *ipAddressPrefix_val_ptr_ptr));
    netsnmp_assert(NULL != ipAddressPrefix_val_ptr_len_ptr);


    DEBUGMSGTL(("verbose:ipAddressTable:ipAddressPrefix_get", "called\n"));

    netsnmp_assert(NULL != rowreq_ctx);

    /*
     * TODO:231:o: |-> Extract the current value of the ipAddressPrefix data.
     * set (* ipAddressPrefix_val_ptr_ptr ) and (* ipAddressPrefix_val_ptr_len_ptr ) from rowreq_ctx->data
     */
    if (NULL == rowreq_ctx->data->ia_prefix_oid) {
        src = nullOid;
        len = nullOidLen / sizeof(oid);
    } else {
        src = rowreq_ctx->data->ia_prefix_oid;
        len = rowreq_ctx->data->ia_prefix_oid_len;
    }
    if ((*ipAddressPrefix_val_ptr_len_ptr) < len) {
        (*ipAddressPrefix_val_ptr_ptr) =
            malloc(len * sizeof((*ipAddressPrefix_val_ptr_ptr)[0]));
        if (NULL == (*ipAddressPrefix_val_ptr_ptr)) {
            snmp_log(LOG_ERR, "could not allocate memory\n");
            return MFD_ERROR;
        }
    }
    (*ipAddressPrefix_val_ptr_len_ptr) = len;
    memcpy((*ipAddressPrefix_val_ptr_ptr), src,
           len * sizeof((*ipAddressPrefix_val_ptr_ptr)[0]));

    return MFD_SUCCESS;
}                               /* ipAddressPrefix_get */

/*---------------------------------------------------------------------
 * IP-MIB::ipAddressEntry.ipAddressOrigin
 * ipAddressOrigin is subid 6 of ipAddressEntry.
 * Its status is Current, and its access level is ReadOnly.
 * OID: .1.3.6.1.2.1.4.34.1.6
 * Description:
The origin of the address.
 *
 * Attributes:
 *   accessible 1     isscalar 0     enums  1      hasdefval 0
 *   readable   1     iscolumn 1     ranges 0      hashint   0
 *   settable   0
 *
 * Enum range: 4/8. Values:  other(1), manual(2), dhcp(4), linklayer(5), random(6)
 *
 * Its syntax is IpAddressOriginTC (based on perltype INTEGER)
 * The net-snmp type is ASN_INTEGER. The C type decl is long (u_long)
 */
/**
 * Extract the current value of the ipAddressOrigin data.
 *
 * Set a value using the data context for the row.
 *
 * @param rowreq_ctx
 *        Pointer to the row request context.
 * @param ipAddressOrigin_val_ptr
 *        Pointer to storage for a long variable
 *
 * @retval MFD_SUCCESS         : success
 * @retval MFD_SKIP            : skip this node (no value for now)
 * @retval MFD_ERROR           : Any other error
 */
int
ipAddressOrigin_get(ipAddressTable_rowreq_ctx * rowreq_ctx,
                    u_long * ipAddressOrigin_val_ptr)
{
   /** we should have a non-NULL pointer */
    netsnmp_assert(NULL != ipAddressOrigin_val_ptr);


    DEBUGMSGTL(("verbose:ipAddressTable:ipAddressOrigin_get", "called\n"));

    netsnmp_assert(NULL != rowreq_ctx);

    /*
     * TODO:231:o: |-> Extract the current value of the ipAddressOrigin data.
     * set (* ipAddressOrigin_val_ptr ) from rowreq_ctx->data
     */
    (*ipAddressOrigin_val_ptr) = rowreq_ctx->data->ia_origin;

    return MFD_SUCCESS;
}                               /* ipAddressOrigin_get */

/*---------------------------------------------------------------------
 * IP-MIB::ipAddressEntry.ipAddressStatus
 * ipAddressStatus is subid 7 of ipAddressEntry.
 * Its status is Current, and its access level is Create.
 * OID: .1.3.6.1.2.1.4.34.1.7
 * Description:
The status of the address, describing if the address can be
            used for communication.


            In the absence of other information, an IPv4 address is
            always preferred(1).
 *
 * Attributes:
 *   accessible 1     isscalar 0     enums  1      hasdefval 1
 *   readable   1     iscolumn 1     ranges 0      hashint   0
 *   settable   1
 *   defval: preferred
 *
 * Enum range: 5/8. Values:  preferred(1), invalid(3), inaccessible(4), unknown(5), tentative(6), duplicate(7)
 *
 * Its syntax is IpAddressStatusTC (based on perltype INTEGER)
 * The net-snmp type is ASN_INTEGER. The C type decl is long (u_long)
 */
/**
 * Extract the current value of the ipAddressStatus data.
 *
 * Set a value using the data context for the row.
 *
 * @param rowreq_ctx
 *        Pointer to the row request context.
 * @param ipAddressStatus_val_ptr
 *        Pointer to storage for a long variable
 *
 * @retval MFD_SUCCESS         : success
 * @retval MFD_SKIP            : skip this node (no value for now)
 * @retval MFD_ERROR           : Any other error
 */
int
ipAddressStatus_get(ipAddressTable_rowreq_ctx * rowreq_ctx,
                    u_long * ipAddressStatus_val_ptr)
{
   /** we should have a non-NULL pointer */
    netsnmp_assert(NULL != ipAddressStatus_val_ptr);


    DEBUGMSGTL(("verbose:ipAddressTable:ipAddressStatus_get", "called\n"));

    netsnmp_assert(NULL != rowreq_ctx);

    /*
     * TODO:231:o: |-> Extract the current value of the ipAddressStatus data.
     * set (* ipAddressStatus_val_ptr ) from rowreq_ctx->data
     */
    (*ipAddressStatus_val_ptr) = rowreq_ctx->data->ia_status;

    return MFD_SUCCESS;
}                               /* ipAddressStatus_get */

/*---------------------------------------------------------------------
 * IP-MIB::ipAddressEntry.ipAddressCreated
 * ipAddressCreated is subid 8 of ipAddressEntry.
 * Its status is Current, and its access level is ReadOnly.
 * OID: .1.3.6.1.2.1.4.34.1.8
 * Description:
The value of sysUpTime at the time this entry was created.
            If this entry was created prior to the last re-
            initialization of the local network management subsystem,
            then this object contains a zero value.
 *
 * Attributes:
 *   accessible 1     isscalar 0     enums  0      hasdefval 0
 *   readable   1     iscolumn 1     ranges 0      hashint   0
 *   settable   0
 *
 *
 * Its syntax is TimeStamp (based on perltype TICKS)
 * The net-snmp type is ASN_TIMETICKS. The C type decl is u_long (u_long)
 */
/**
 * Extract the current value of the ipAddressCreated data.
 *
 * Set a value using the data context for the row.
 *
 * @param rowreq_ctx
 *        Pointer to the row request context.
 * @param ipAddressCreated_val_ptr
 *        Pointer to storage for a u_long variable
 *
 * @retval MFD_SUCCESS         : success
 * @retval MFD_SKIP            : skip this node (no value for now)
 * @retval MFD_ERROR           : Any other error
 */
int
ipAddressCreated_get(ipAddressTable_rowreq_ctx * rowreq_ctx,
                     u_long * ipAddressCreated_val_ptr)
{
   /** we should have a non-NULL pointer */
    netsnmp_assert(NULL != ipAddressCreated_val_ptr);


    DEBUGMSGTL(("verbose:ipAddressTable:ipAddressCreated_get",
                "called\n"));

    netsnmp_assert(NULL != rowreq_ctx);

    /*
     * TODO:231:o: |-> Extract the current value of the ipAddressCreated data.
     * set (* ipAddressCreated_val_ptr ) from rowreq_ctx->data
     */
    (*ipAddressCreated_val_ptr) = rowreq_ctx->ipAddressCreated;

    return MFD_SUCCESS;
}                               /* ipAddressCreated_get */

/*---------------------------------------------------------------------
 * IP-MIB::ipAddressEntry.ipAddressLastChanged
 * ipAddressLastChanged is subid 9 of ipAddressEntry.
 * Its status is Current, and its access level is ReadOnly.
 * OID: .1.3.6.1.2.1.4.34.1.9
 * Description:
The value of sysUpTime at the time this entry was last
            updated.  If this entry was updated prior to the last re-
            initialization of the local network management subsystem,
            then this object contains a zero value.
 *
 * Attributes:
 *   accessible 1     isscalar 0     enums  0      hasdefval 0
 *   readable   1     iscolumn 1     ranges 0      hashint   0
 *   settable   0
 *
 *
 * Its syntax is TimeStamp (based on perltype TICKS)
 * The net-snmp type is ASN_TIMETICKS. The C type decl is u_long (u_long)
 */
/**
 * Extract the current value of the ipAddressLastChanged data.
 *
 * Set a value using the data context for the row.
 *
 * @param rowreq_ctx
 *        Pointer to the row request context.
 * @param ipAddressLastChanged_val_ptr
 *        Pointer to storage for a u_long variable
 *
 * @retval MFD_SUCCESS         : success
 * @retval MFD_SKIP            : skip this node (no value for now)
 * @retval MFD_ERROR           : Any other error
 */
int
ipAddressLastChanged_get(ipAddressTable_rowreq_ctx * rowreq_ctx,
                         u_long * ipAddressLastChanged_val_ptr)
{
   /** we should have a non-NULL pointer */
    netsnmp_assert(NULL != ipAddressLastChanged_val_ptr);


    DEBUGMSGTL(("verbose:ipAddressTable:ipAddressLastChanged_get",
                "called\n"));

    netsnmp_assert(NULL != rowreq_ctx);

    /*
     * TODO:231:o: |-> Extract the current value of the ipAddressLastChanged data.
     * set (* ipAddressLastChanged_val_ptr ) from rowreq_ctx->data
     */
    (*ipAddressLastChanged_val_ptr) = rowreq_ctx->ipAddressLastChanged;

    return MFD_SUCCESS;
}                               /* ipAddressLastChanged_get */

/*---------------------------------------------------------------------
 * IP-MIB::ipAddressEntry.ipAddressRowStatus
 * ipAddressRowStatus is subid 10 of ipAddressEntry.
 * Its status is Current, and its access level is Create.
 * OID: .1.3.6.1.2.1.4.34.1.10
 * Description:
The status of this conceptual row.


            The RowStatus TC requires that this DESCRIPTION clause
            states under which circumstances other objects in this row
            can be modified.  The value of this object has no effect on
            whether other objects in this conceptual row can be
            modified.


            A conceptual row can not be made active until the
            ipAddressIfIndex has been set to a valid index.  
 *
 * Attributes:
 *   accessible 1     isscalar 0     enums  1      hasdefval 0
 *   readable   1     iscolumn 1     ranges 0      hashint   0
 *   settable   1
 *
 * Enum range: 3/8. Values:  active(1), notInService(2), notReady(3), createAndGo(4), createAndWait(5), destroy(6)
 *
 * Its syntax is RowStatus (based on perltype INTEGER)
 * The net-snmp type is ASN_INTEGER. The C type decl is long (u_long)
 */
/**
 * Extract the current value of the ipAddressRowStatus data.
 *
 * Set a value using the data context for the row.
 *
 * @param rowreq_ctx
 *        Pointer to the row request context.
 * @param ipAddressRowStatus_val_ptr
 *        Pointer to storage for a long variable
 *
 * @retval MFD_SUCCESS         : success
 * @retval MFD_SKIP            : skip this node (no value for now)
 * @retval MFD_ERROR           : Any other error
 */
int
ipAddressRowStatus_get(ipAddressTable_rowreq_ctx * rowreq_ctx,
                       u_long * ipAddressRowStatus_val_ptr)
{
   /** we should have a non-NULL pointer */
    netsnmp_assert(NULL != ipAddressRowStatus_val_ptr);

    /** WARNING: this code might not work for netsnmp_ipaddress_entry */
    (*ipAddressRowStatus_val_ptr) = rowreq_ctx->ipAddressRowStatus;

    return MFD_SUCCESS;
}                               /* ipAddressRowStatus_get */

/*---------------------------------------------------------------------
 * IP-MIB::ipAddressEntry.ipAddressStorageType
 * ipAddressStorageType is subid 11 of ipAddressEntry.
 * Its status is Current, and its access level is Create.
 * OID: .1.3.6.1.2.1.4.34.1.11
 * Description:
The storage type for this conceptual row.  If this object
            has a value of 'permanent' then no other objects are
            required to be able to be modified.
 *
 * Attributes:
 *   accessible 1     isscalar 0     enums  1      hasdefval 1
 *   readable   1     iscolumn 1     ranges 0      hashint   0
 *   settable   1
 *   defval: volatile
 *
 * Enum range: 4/8. Values:  other(1), volatile(2), nonVolatile(3), permanent(4), readOnly(5)
 *
 * Its syntax is StorageType (based on perltype INTEGER)
 * The net-snmp type is ASN_INTEGER. The C type decl is long (u_long)
 */
/**
 * Extract the current value of the ipAddressStorageType data.
 *
 * Set a value using the data context for the row.
 *
 * @param rowreq_ctx
 *        Pointer to the row request context.
 * @param ipAddressStorageType_val_ptr
 *        Pointer to storage for a long variable
 *
 * @retval MFD_SUCCESS         : success
 * @retval MFD_SKIP            : skip this node (no value for now)
 * @retval MFD_ERROR           : Any other error
 */
int
ipAddressStorageType_get(ipAddressTable_rowreq_ctx * rowreq_ctx,
                         u_long * ipAddressStorageType_val_ptr)
{
   /** we should have a non-NULL pointer */
    netsnmp_assert(NULL != ipAddressStorageType_val_ptr);


    DEBUGMSGTL(("verbose:ipAddressTable:ipAddressStorageType_get",
                "called\n"));

    netsnmp_assert(NULL != rowreq_ctx);

    /*
     * TODO:231:o: |-> Extract the current value of the ipAddressStorageType data.
     * set (* ipAddressStorageType_val_ptr ) from rowreq_ctx->data
     */
    (*ipAddressStorageType_val_ptr) = rowreq_ctx->data->ia_storagetype;

    return MFD_SUCCESS;
}                               /* ipAddressStorageType_get */


/** @} */
/**********************************************************************
 **********************************************************************
 ***
 *** Table ipAddressTable
 ***
 **********************************************************************
 **********************************************************************/
/*
 * ipAddressTable is subid 34 of ip.
 * Its status is Current.
 * OID: .1.3.6.1.2.1.4.34, length: 8
 */
    /*
     * NOTE: if you update this chart, please update the versions in
     *       local/mib2c-conf.d/parent-set.m2i
     *       agent/mibgroup/helpers/baby_steps.c
     * while you're at it.
     */
    /*
     ***********************************************************************
     * Baby Steps Flow Chart (2004.06.05)                                  *
     *                                                                     *
     * +--------------+    +================+    U = unconditional path    *
     * |optional state|    ||required state||    S = path for success      *
     * +--------------+    +================+    E = path for error        *
     ***********************************************************************
     *
     *                        +--------------+
     *                        |     pre      |
     *                        |   request    |
     *                        +--------------+
     *                               | U
     * +-------------+        +==============+
     * |    row    |f|<-------||  object    ||
     * |  create   |1|      E ||  lookup    ||
     * +-------------+        +==============+
     *     E |   | S                 | S
     *       |   +------------------>|
     *       |                +==============+
     *       |              E ||   check    ||
     *       |<---------------||   values   ||
     *       |                +==============+
     *       |                       | S
     *       |                +==============+
     *       |       +<-------||   undo     ||
     *       |       |      E ||   setup    ||
     *       |       |        +==============+
     *       |       |               | S
     *       |       |        +==============+
     *       |       |        ||    set     ||-------------------------->+
     *       |       |        ||   value    || E                         |
     *       |       |        +==============+                           |
     *       |       |               | S                                 |
     *       |       |        +--------------+                           |
     *       |       |        |    check     |-------------------------->|
     *       |       |        |  consistency | E                         |
     *       |       |        +--------------+                           |
     *       |       |               | S                                 |
     *       |       |        +==============+         +==============+  |
     *       |       |        ||   commit   ||-------->||     undo   ||  |
     *       |       |        ||            || E       ||    commit  ||  |
     *       |       |        +==============+         +==============+  |
     *       |       |               | S                     U |<--------+
     *       |       |        +--------------+         +==============+
     *       |       |        | irreversible |         ||    undo    ||
     *       |       |        |    commit    |         ||     set    ||
     *       |       |        +--------------+         +==============+
     *       |       |               | U                     U |
     *       |       +-------------->|<------------------------+
     *       |                +==============+
     *       |                ||   undo     ||
     *       |                ||  cleanup   ||
     *       |                +==============+
     *       +---------------------->| U
     *                               |
     *                          (err && f1)------------------->+
     *                               |                         |
     *                        +--------------+         +--------------+
     *                        |    post      |<--------|      row     |
     *                        |   request    |       U |    release   |
     *                        +--------------+         +--------------+
     *
     */

/**
 * verify specified index is valid.
 *
 * This check is independent of whether or not the values specified for
 * the columns of the new row are valid. Column values and row consistency
 * will be checked later. At this point, only the index values should be
 * checked.
 *
 * All of the individual index validation functions have been called, so this
 * is the place to make sure they are valid as a whole when combined. If
 * you only have one index, 
 * 
 *
 *
 * @param ipAddressTable_reg
 *        Pointer to the user registration data
 * @param ipAddressTable_rowreq_ctx
 *        Pointer to the users context.
 * @retval MFD_SUCCESS            : success
 * @retval MFD_CANNOT_CREATE_NOW  : index not valid right now
 * @retval MFD_CANNOT_CREATE_EVER : index never valid
 */
int
ipAddressTable_validate_index(ipAddressTable_registration_ptr
                              ipAddressTable_reg,
                              ipAddressTable_rowreq_ctx * rowreq_ctx)
{
    int             rc = MFD_SUCCESS;

    DEBUGMSGTL(("verbose:ipAddressTable:ipAddressTable_validate_index",
                "called\n"));

    /** we should have a non-NULL pointer */
    netsnmp_assert(NULL != rowreq_ctx);

    /*
     * TODO:430:M: |-> Validate potential ipAddressTable index.
     *
     * xxx-rks: we only plan ipv4 support initially
     */
    if ((4 != rowreq_ctx->tbl_idx.ipAddressAddr_len)) {
        snmp_log(LOG_WARNING, "invalid index for a new row in the "
                 "ipAddressTable table.\n");
        /*
         * determine failure type.
         *
         * If the index could not ever be created, return MFD_NOT_EVER
         * If the index can not be created under the present circumstances
         * (even though it could be created under other circumstances),
         * return MFD_NOT_NOW.
         */
        if (0) {
            return MFD_CANNOT_CREATE_EVER;
        } else {
            return MFD_CANNOT_CREATE_NOW;
        }
    } else {
        rowreq_ctx->data->ia_address[0] =
            rowreq_ctx->tbl_idx.ipAddressAddr[0];
        rowreq_ctx->data->ia_address[1] =
            rowreq_ctx->tbl_idx.ipAddressAddr[1];
        rowreq_ctx->data->ia_address[2] =
            rowreq_ctx->tbl_idx.ipAddressAddr[2];
        rowreq_ctx->data->ia_address[3] =
            rowreq_ctx->tbl_idx.ipAddressAddr[3];
        rowreq_ctx->data->ia_address_len = 4;
    }

    return rc;
}                               /* ipAddressTable_validate_index */

/**
 * Setup up context with information needed to undo a set request.
 *
 * This function will be called before the individual node undo setup
 * functions are called. If you need to do any undo setup that is not
 * related to a specific column, you can do it here.
 *
 * Note that an individual node's undo_setup function will only be called
 * if that node is being set to a new value.
 *
 * If there is any setup specific to a particular column (e.g. allocating
 * memory for a string), you should do that setup in the node's undo_setup
 * function, so it won't be done unless it is necessary.
 *
 * @param rowreq_ctx
 *        Pointer to the table context (ipAddressTable_rowreq_ctx)
 *
 * @retval MFD_SUCCESS : success
 * @retval MFD_ERROR   : error. set will fail.
 */
int
ipAddressTable_undo_setup(ipAddressTable_rowreq_ctx * rowreq_ctx)
{
    int             rc = MFD_SUCCESS;

    DEBUGMSGTL(("verbose:ipAddressTable:ipAddressTable_undo_setup",
                "called\n"));

    /** we should have a non-NULL pointer */
    netsnmp_assert(NULL != rowreq_ctx);

    /*
     * TODO:451:M: |-> Setup ipAddressTable undo.
     * set up ipAddressTable undo information, in preparation for a set.
     */
    /*
     * check for storage types that don't allow modification.
     * probably should try and do this earlier (and we could, by
     * adding code to the interface file), but this ought to suffice.
     */
    if (STORAGETYPE_READONLY == rowreq_ctx->data->ia_storagetype) {
        DEBUGMSGTL(("ipAddressTable", "can't change readonly row\n"));
        return MFD_NOT_VALID_EVER;
    }

    /*
     * save last changed
     */
    rowreq_ctx->ipAddressLastChanged_undo =
        rowreq_ctx->ipAddressLastChanged;


    rowreq_ctx->ipAddressLastChanged =
        rowreq_ctx->ipAddressLastChanged_undo;

    /*
     * just copy everything
     */
    rc = netsnmp_access_ipaddress_entry_copy(rowreq_ctx->undo,
                                             rowreq_ctx->data);

    return rc;
}                               /* ipAddressTable_undo_setup */

/**
 * Cleanup up context undo information.
 *
 * This function will be called after set/commit processing. If you
 * allocated any resources in undo_setup, this is the place to release
 * those resources.
 *
 * This function is called regardless of the success or failure of the set
 * request. If you need to perform different steps for cleanup depending
 * on success or failure, you can add a flag to the rowreq_ctx.
 *
 * @param rowreq_ctx
 *        Pointer to the table context (ipAddressTable_rowreq_ctx)
 *
 * @retval MFD_SUCCESS : success
 * @retval MFD_ERROR   : error
 */
int
ipAddressTable_undo_cleanup(ipAddressTable_rowreq_ctx * rowreq_ctx)
{
    int             rc = MFD_SUCCESS;

    DEBUGMSGTL(("verbose:ipAddressTable:ipAddressTable_undo_cleanup",
                "called\n"));

    /** we should have a non-NULL pointer */
    netsnmp_assert(NULL != rowreq_ctx);

    /*
     * TODO:452:M: |-> Cleanup ipAddressTable undo.
     */

    return rc;
}                               /* ipAddressTable_undo_cleanup */

/**
 * commit new values.
 *
 * At this point, you should have done everything you can to ensure that
 * this commit will not fail.
 *
 * Should you need different behavior depending on which columns were
 * set, rowreq_ctx->column_set_flags will indicate which writeable columns were
 * set. The definitions for the FLAG_* bits can be found in
 * ipAddressTable.h.
 * A new row will have the MFD_ROW_CREATED bit set in rowreq_flags.
 *
 * @param ipAddressTable_rowreq_ctx
 *        Pointer to the users context.
 *
 * @retval MFD_SUCCESS : success
 * @retval MFD_ERROR   : error
 */
int
ipAddressTable_commit(ipAddressTable_rowreq_ctx * rowreq_ctx)
{
    int             rc = MFD_SUCCESS;
    int             save_flags;

    DEBUGMSGTL(("verbose:ipAddressTable:ipAddressTable_commit",
                "called\n"));

    /** we should have a non-NULL pointer */
    netsnmp_assert(NULL != rowreq_ctx);

    /*
     * save flags, then clear until we actually do something
     */
    save_flags = rowreq_ctx->column_set_flags;
    rowreq_ctx->column_set_flags = 0;

    /*
     * commit ipAddressTable data
     * 1) check the column's flag in save_flags to see if it was set.
     * 2) clear the flag when you handle that column
     * 3) set the column's flag in column_set_flags if it needs undo
     *    processing in case of a failure.
     */
    /*
     * did anything change?
     */
    if (0 == save_flags) {
        DEBUGMSGTL(("ipAddressTable:ipAddressTable_commit",
                    "no change\n"));
        return MFD_SUCCESS;
    }

    /*
     * pass everything to data access
     * let data access know what columns are set
     */
    rowreq_ctx->data->flags = save_flags;

    if (save_flags & FLAG_IPADDRESSROWSTATUS) {
        if (rowreq_ctx->rowreq_flags & MFD_ROW_CREATED) {
            netsnmp_assert(ROWSTATUS_CREATEANDGO ==
                           rowreq_ctx->ipAddressRowStatus);
            rowreq_ctx->data->flags |= NETSNMP_ACCESS_IPADDRESS_CREATE;
            rowreq_ctx->ipAddressCreated = netsnmp_get_agent_uptime();
        } else if (ROWSTATUS_DESTROY == rowreq_ctx->ipAddressRowStatus) {
            rowreq_ctx->data->flags |= NETSNMP_ACCESS_IPADDRESS_DELETE;
        } else
            rowreq_ctx->data->flags |= NETSNMP_ACCESS_IPADDRESS_CHANGE;
    } else
        rowreq_ctx->data->flags |= NETSNMP_ACCESS_IPADDRESS_CHANGE;

    /*
     * do it
     */
    rc = netsnmp_access_ipaddress_entry_set(rowreq_ctx->data);
    if (rc) {
        DEBUGMSGTL(("ipAddressTable",
                    "bad rc %d from IP address data access\n", rc));
        rc = MFD_ERROR;
    } else {
        rowreq_ctx->ipAddressLastChanged = netsnmp_get_agent_uptime();
        /*
         * set flag, in case we need to undo
         */
        rowreq_ctx->column_set_flags |= save_flags;
    }

    return rc;
}                               /* ipAddressTable_commit */

/**
 * undo commit new values.
 *
 * Should you need different behavior depending on which columns were
 * set, rowreq_ctx->column_set_flags will indicate which writeable columns were
 * set. The definitions for the FLAG_* bits can be found in
 * ipAddressTable.h.
 * A new row will have the MFD_ROW_CREATED bit set in rowreq_flags.
 *
 * @param ipAddressTable_rowreq_ctx
 *        Pointer to the users context.
 *
 * @retval MFD_SUCCESS : success
 * @retval MFD_ERROR   : error
 */
int
ipAddressTable_undo_commit(ipAddressTable_rowreq_ctx * rowreq_ctx)
{
    int             rc = MFD_SUCCESS;

    DEBUGMSGTL(("verbose:ipAddressTable:ipAddressTable_undo_commit",
                "called\n"));

    /** we should have a non-NULL pointer */
    netsnmp_assert(NULL != rowreq_ctx);

    /*
     * TODO:485:M: |-> Undo ipAddressTable commit.
     * check the column's flag in rowreq_ctx->column_set_flags to see
     * if it was set during commit, then undo it.
     */
    if (rowreq_ctx->column_set_flags & FLAG_IPADDRESSROWSTATUS) {
        /*
         * if we created an addr, delete it. if we deleted it,
         * re-create it. If we changed it, change it back.
         */
        if (rowreq_ctx->rowreq_flags & MFD_ROW_CREATED) {
            rowreq_ctx->undo->flags |= NETSNMP_ACCESS_IPADDRESS_DELETE;
        } else if (ROWSTATUS_DESTROY == rowreq_ctx->ipAddressRowStatus) {
            rowreq_ctx->undo->flags |= NETSNMP_ACCESS_IPADDRESS_CREATE;
        } else
            rowreq_ctx->undo->flags |= NETSNMP_ACCESS_IPADDRESS_CHANGE;
    } else
        rowreq_ctx->undo->flags |= NETSNMP_ACCESS_IPADDRESS_CHANGE;

    /*
     * do it
     */
    rc = netsnmp_access_ipaddress_entry_set(rowreq_ctx->undo);
    if (rc) {
        DEBUGMSGTL(("ipAddressTable",
                    "bad rc %d from IP address data access\n", rc));
        rc = MFD_ERROR;
    }

    return rc;
}                               /* ipAddressTable_undo_commit */

/*
 * TODO:420:r: Implement ipAddressTable index validation.
 */
/*---------------------------------------------------------------------
 * IP-MIB::ipAddressEntry.ipAddressAddrType
 * ipAddressAddrType is subid 1 of ipAddressEntry.
 * Its status is Current, and its access level is NoAccess.
 * OID: .1.3.6.1.2.1.4.34.1.1
 * Description:
The address type of ipAddressAddr.
 *
 * Attributes:
 *   accessible 0     isscalar 0     enums  1      hasdefval 0
 *   readable   0     iscolumn 1     ranges 0      hashint   0
 *   settable   0
 *
 * Enum range: 5/8. Values:  unknown(0), ipv4(1), ipv6(2), ipv4z(3), ipv6z(4), dns(16)
 *
 * Its syntax is InetAddressType (based on perltype INTEGER)
 * The net-snmp type is ASN_INTEGER. The C type decl is long (u_long)
 *
 *
 *
 * NOTE: NODE ipAddressAddrType IS NOT ACCESSIBLE
 *
 *
 */
/**
 * check validity of ipAddressAddrType index portion
 *
 * @retval MFD_SUCCESS   : the incoming value is legal
 * @retval MFD_BAD_VALUE : the incoming value is NOT legal
 *
 * @note this is not the place to do any checks for the sanity
 *       of multiple indexes. Those types of checks should be done in the
 *       ipAddressTable_validate_index() function.
 */
int
ipAddressAddrType_check_index(ipAddressTable_rowreq_ctx * rowreq_ctx)
{
    DEBUGMSGTL(("verbose:ipAddressTable:ipAddressAddrType_check_index",
                "called\n"));

    netsnmp_assert(NULL != rowreq_ctx);

    /*
     * TODO:426:M: |-> Check ipAddressTable index ipAddressAddrType.
     * check that index value in the table context (rowreq_ctx)
     * for ipAddressAddrType is legal.
     */
    switch (rowreq_ctx->tbl_idx.ipAddressAddrType) {

    case INETADDRESSTYPE_IPV4:
    case INETADDRESSTYPE_IPV6:
        break;

    default:
        DEBUGMSGT(("ipAddressTable", "illegal addr type\n"));
        return MFD_ERROR;
    }

    return MFD_SUCCESS;         /* ipAddressAddrType index ok */
}                               /* ipAddressAddrType_check_index */

/*---------------------------------------------------------------------
 * IP-MIB::ipAddressEntry.ipAddressAddr
 * ipAddressAddr is subid 2 of ipAddressEntry.
 * Its status is Current, and its access level is NoAccess.
 * OID: .1.3.6.1.2.1.4.34.1.2
 * Description:
The IP address to which this entry's addressing information
            pertains.  The address type of this object is specified in
            ipAddressAddrType.


            Implementors need to be aware that if the size of
            ipAddressAddr exceeds 116 octets then OIDS of instances of
            columns in this row will have more than 128 sub-identifiers
            and cannot be accessed using SNMPv1, SNMPv2c or SNMPv3.
 *
 * Attributes:
 *   accessible 0     isscalar 0     enums  0      hasdefval 0
 *   readable   0     iscolumn 1     ranges 1      hashint   0
 *   settable   0
 *
 * Ranges:  0 - 255;
 *
 * Its syntax is InetAddress (based on perltype OCTETSTR)
 * The net-snmp type is ASN_OCTET_STR. The C type decl is char (char)
 * This data type requires a length.  (Max 255)
 *
 *
 *
 * NOTE: NODE ipAddressAddr IS NOT ACCESSIBLE
 *
 *
 */
/**
 * check validity of ipAddressAddr index portion
 *
 * @retval MFD_SUCCESS   : the incoming value is legal
 * @retval MFD_BAD_VALUE : the incoming value is NOT legal
 *
 * @note this is not the place to do any checks for the sanity
 *       of multiple indexes. Those types of checks should be done in the
 *       ipAddressTable_validate_index() function.
 */
int
ipAddressAddr_check_index(ipAddressTable_rowreq_ctx * rowreq_ctx)
{
    DEBUGMSGTL(("verbose:ipAddressTable:ipAddressAddr_check_index",
                "called\n"));

    netsnmp_assert(NULL != rowreq_ctx);

    /*
     * TODO:426:M: |-> Check ipAddressTable index ipAddressAddr.
     * check that index value in the table context (rowreq_ctx)
     * for ipAddressAddr is legal.
     */
    switch (rowreq_ctx->tbl_idx.ipAddressAddrType) {

    case INETADDRESSTYPE_IPV4:
        if (4 != rowreq_ctx->tbl_idx.ipAddressAddr_len) {
            DEBUGMSGT(("ipAddressTable", "bad addr len\n"));
            return MFD_ERROR;
        }
        break;

    case INETADDRESSTYPE_IPV6:
            /** xxx-rks: allow 20? */
        if (16 != rowreq_ctx->tbl_idx.ipAddressAddr_len) {
            DEBUGMSGT(("ipAddressTable", "bad addr len\n"));
            return MFD_ERROR;
        }
        break;

    default:
        return MFD_ERROR;
    }

    return MFD_SUCCESS;         /* ipAddressAddr index ok */
}                               /* ipAddressAddr_check_index */

/*
 * TODO:440:M: Implement ipAddressTable node value checks.
 * TODO:450:M: Implement ipAddressTable undo functions.
 * TODO:460:M: Implement ipAddressTable set functions.
 * TODO:480:M: Implement ipAddressTable commit functions.
 */
/*---------------------------------------------------------------------
 * IP-MIB::ipAddressEntry.ipAddressIfIndex
 * ipAddressIfIndex is subid 3 of ipAddressEntry.
 * Its status is Current, and its access level is Create.
 * OID: .1.3.6.1.2.1.4.34.1.3
 * Description:
The index value which uniquely identifies the interface to
            which this entry is applicable.  The interface identified by
            a particular value of this index is the same interface as
            identified by the same value of the IF-MIB's ifIndex.
 *
 * Attributes:
 *   accessible 1     isscalar 0     enums  0      hasdefval 0
 *   readable   1     iscolumn 1     ranges 1      hashint   1
 *   settable   1
 *   hint: d
 *
 * Ranges:  1 - 2147483647;
 *
 * Its syntax is InterfaceIndex (based on perltype INTEGER32)
 * The net-snmp type is ASN_INTEGER. The C type decl is long (long)
 */
/**
 * Check that the proposed new value is potentially valid.
 *
 * @param rowreq_ctx
 *        Pointer to the row request context.
 * @param ipAddressIfIndex_val
 *        A long containing the new value.
 *
 * @retval MFD_SUCCESS        : incoming value is legal
 * @retval MFD_NOT_VALID_NOW  : incoming value is not valid now
 * @retval MFD_NOT_VALID_EVER : incoming value is never valid
 *
 * This is the place to check for requirements that are not
 * expressed in the mib syntax (for example, a requirement that
 * is detailed in the description for an object).
 *
 * You should check that the requested change between the undo value and the
 * new value is legal (ie, the transistion from one value to another
 * is legal).
 *      
 *@note
 * This check is only to determine if the new value
 * is \b potentially valid. This is the first check of many, and
 * is one of the simplest ones.
 * 
 *@note
 * this is not the place to do any checks for values
 * which depend on some other value in the mib. Those
 * types of checks should be done in the
 * ipAddressTable_check_dependencies() function.
 *
 * The following checks have already been done for you:
 *    The syntax is ASN_INTEGER
 *    The value is in (one of) the range set(s):  1 - 2147483647
 *
 * If there a no other checks you need to do, simply return MFD_SUCCESS.
 *
 */
int
ipAddressIfIndex_check_value(ipAddressTable_rowreq_ctx * rowreq_ctx,
                             long ipAddressIfIndex_val)
{
    DEBUGMSGTL(("verbose:ipAddressTable:ipAddressIfIndex_check_value",
                "called\n"));

    /** should never get a NULL pointer */
    netsnmp_assert(NULL != rowreq_ctx);

    /*
     * TODO:441:o: |-> Check for valid ipAddressIfIndex value.
     */
    /*
     * if the new value is the same as the old, accept it.
     */
    if (ipAddressIfIndex_val == rowreq_ctx->data->if_index)
        return MFD_SUCCESS;

    /*
     * currently don't support moving addresses between interfaces, so
     * if this isn't a new row, return error.
     */
    if (!(rowreq_ctx->rowreq_flags & MFD_ROW_CREATED)) {
        DEBUGMSGT(("ipAddressTable",
                   "changing ifIndex value not supported\n"));
        return MFD_NOT_VALID_EVER;
    }

    /*
     * find name for ifIndex
     */
    if (NULL == netsnmp_access_interface_name_find(ipAddressIfIndex_val)) {
        DEBUGMSGT(("ipAddressTable", "cant find name for index %d\n",
                   ipAddressIfIndex_val));
        return MFD_NOT_VALID_NOW;
    }

    return MFD_SUCCESS;         /* ipAddressIfIndex value not illegal */
}                               /* ipAddressIfIndex_check_value */

/**
 * Save old value information
 *
 * @param rowreq_ctx
 *        Pointer to the table context (ipAddressTable_rowreq_ctx)
 *
 * @retval MFD_SUCCESS : success
 * @retval MFD_ERROR   : error. set will fail.
 *
 * This function will be called after the table level undo setup function
 * ipAddressTable_undo_setup has been called.
 *
 *@note
 * this function will only be called if a new value is set for this column.
 *
 * If there is any setup specific to a particular column (e.g. allocating
 * memory for a string), you should do that setup in this function, so it
 * won't be done unless it is necessary.
 */
int
ipAddressIfIndex_undo_setup(ipAddressTable_rowreq_ctx * rowreq_ctx)
{
    DEBUGMSGTL(("verbose:ipAddressTable:ipAddressIfIndex_undo_setup",
                "called\n"));

    /** should never get a NULL pointer */
    netsnmp_assert(NULL != rowreq_ctx);

    /*
     * TODO:455:o: |-> Setup ipAddressIfIndex undo.
     */
    /*
     * handled in ipAddressTable_undo_setup
     */

    return MFD_SUCCESS;
}                               /* ipAddressIfIndex_undo_setup */

/**
 * Set the new value.
 *
 * @param rowreq_ctx
 *        Pointer to the users context. You should know how to
 *        manipulate the value from this object.
 * @param ipAddressIfIndex_val
 *        A long containing the new value.
 */
int
ipAddressIfIndex_set(ipAddressTable_rowreq_ctx * rowreq_ctx,
                     long ipAddressIfIndex_val)
{

    DEBUGMSGTL(("verbose:ipAddressTable:ipAddressIfIndex_set",
                "called\n"));

    /** should never get a NULL pointer */
    netsnmp_assert(NULL != rowreq_ctx);

    /*
     * TODO:461:M: |-> Set ipAddressIfIndex value.
     * set ipAddressIfIndex value in rowreq_ctx->data
     */
    if (rowreq_ctx->data->if_index != ipAddressIfIndex_val)
        rowreq_ctx->data->if_index = ipAddressIfIndex_val;
    else
        rowreq_ctx->column_set_flags &= ~FLAG_IPADDRESSIFINDEX;

    return MFD_SUCCESS;
}                               /* ipAddressIfIndex_set */

/**
 * undo the previous set.
 *
 * @param rowreq_ctx
 *        Pointer to the users context.
 */
int
ipAddressIfIndex_undo(ipAddressTable_rowreq_ctx * rowreq_ctx)
{

    DEBUGMSGTL(("verbose:ipAddressTable:ipAddressIfIndex_undo",
                "called\n"));

    netsnmp_assert(NULL != rowreq_ctx);

    /*
     * TODO:456:o: |-> Clean up ipAddressIfIndex undo.
     */
    /*
     * copy ipAddressIfIndex data
     * set rowreq_ctx->data->ipAddressIfIndex from rowreq_ctx->undo->ipAddressIfIndex
     */
    rowreq_ctx->data->if_index = rowreq_ctx->undo->if_index;

    return MFD_SUCCESS;
}                               /* ipAddressIfIndex_undo */

/*---------------------------------------------------------------------
 * IP-MIB::ipAddressEntry.ipAddressType
 * ipAddressType is subid 4 of ipAddressEntry.
 * Its status is Current, and its access level is Create.
 * OID: .1.3.6.1.2.1.4.34.1.4
 * Description:
The type of address.  broadcast(3) is not a valid value for
            IPv6 addresses (RFC3513).  
 *
 * Attributes:
 *   accessible 1     isscalar 0     enums  1      hasdefval 1
 *   readable   1     iscolumn 1     ranges 0      hashint   0
 *   settable   1
 *   defval: unicast
 *
 * Enum range: 2/8. Values:  unicast(1), anycast(2), broadcast(3)
 *
 * Its syntax is INTEGER (based on perltype INTEGER)
 * The net-snmp type is ASN_INTEGER. The C type decl is long (u_long)
 */
/**
 * Check that the proposed new value is potentially valid.
 *
 * @param rowreq_ctx
 *        Pointer to the row request context.
 * @param ipAddressType_val
 *        A long containing the new value.
 *
 * @retval MFD_SUCCESS        : incoming value is legal
 * @retval MFD_NOT_VALID_NOW  : incoming value is not valid now
 * @retval MFD_NOT_VALID_EVER : incoming value is never valid
 *
 * This is the place to check for requirements that are not
 * expressed in the mib syntax (for example, a requirement that
 * is detailed in the description for an object).
 *
 * You should check that the requested change between the undo value and the
 * new value is legal (ie, the transistion from one value to another
 * is legal).
 *      
 *@note
 * This check is only to determine if the new value
 * is \b potentially valid. This is the first check of many, and
 * is one of the simplest ones.
 * 
 *@note
 * this is not the place to do any checks for values
 * which depend on some other value in the mib. Those
 * types of checks should be done in the
 * ipAddressTable_check_dependencies() function.
 *
 * The following checks have already been done for you:
 *    The syntax is ASN_INTEGER
 *    The value is one of  unicast(1), anycast(2), broadcast(3)
 *
 * If there a no other checks you need to do, simply return MFD_SUCCESS.
 *
 */
int
ipAddressType_check_value(ipAddressTable_rowreq_ctx * rowreq_ctx,
                          u_long ipAddressType_val)
{
    DEBUGMSGTL(("verbose:ipAddressTable:ipAddressType_check_value",
                "called\n"));

    /** should never get a NULL pointer */
    netsnmp_assert(NULL != rowreq_ctx);

    /*
     * TODO:441:o: |-> Check for valid ipAddressType value.
     *
     * no support for anything but unicast yet
     */
    if (ipAddressType_val != IPADDRESSTYPE_UNICAST)
        return MFD_NOT_VALID_EVER;

    return MFD_SUCCESS;         /* ipAddressType value not illegal */
}                               /* ipAddressType_check_value */

/**
 * Save old value information
 *
 * @param rowreq_ctx
 *        Pointer to the table context (ipAddressTable_rowreq_ctx)
 *
 * @retval MFD_SUCCESS : success
 * @retval MFD_ERROR   : error. set will fail.
 *
 * This function will be called after the table level undo setup function
 * ipAddressTable_undo_setup has been called.
 *
 *@note
 * this function will only be called if a new value is set for this column.
 *
 * If there is any setup specific to a particular column (e.g. allocating
 * memory for a string), you should do that setup in this function, so it
 * won't be done unless it is necessary.
 */
int
ipAddressType_undo_setup(ipAddressTable_rowreq_ctx * rowreq_ctx)
{
    DEBUGMSGTL(("verbose:ipAddressTable:ipAddressType_undo_setup",
                "called\n"));

    /** should never get a NULL pointer */
    netsnmp_assert(NULL != rowreq_ctx);

    /*
     * TODO:455:o: |-> Setup ipAddressType undo.
     */
    /*
     * handled in ipAddressTable_undo_setup
     */

    return MFD_SUCCESS;
}                               /* ipAddressType_undo_setup */

/**
 * Set the new value.
 *
 * @param rowreq_ctx
 *        Pointer to the users context. You should know how to
 *        manipulate the value from this object.
 * @param ipAddressType_val
 *        A long containing the new value.
 */
int
ipAddressType_set(ipAddressTable_rowreq_ctx * rowreq_ctx,
                  u_long ipAddressType_val)
{

    DEBUGMSGTL(("verbose:ipAddressTable:ipAddressType_set", "called\n"));

    /** should never get a NULL pointer */
    netsnmp_assert(NULL != rowreq_ctx);

    /*
     * TODO:461:M: |-> Set ipAddressType value.
     * set ipAddressType value in rowreq_ctx->data
     */
    rowreq_ctx->data->ia_type = ipAddressType_val;

    return MFD_SUCCESS;
}                               /* ipAddressType_set */

/**
 * undo the previous set.
 *
 * @param rowreq_ctx
 *        Pointer to the users context.
 */
int
ipAddressType_undo(ipAddressTable_rowreq_ctx * rowreq_ctx)
{

    DEBUGMSGTL(("verbose:ipAddressTable:ipAddressType_undo", "called\n"));

    netsnmp_assert(NULL != rowreq_ctx);

    /*
     * TODO:456:o: |-> Clean up ipAddressType undo.
     */
    /*
     * copy ipAddressType data
     * set rowreq_ctx->data->ipAddressType from rowreq_ctx->undo->ipAddressType
     */
    rowreq_ctx->data->ia_type = rowreq_ctx->undo->ia_type;

    return MFD_SUCCESS;
}                               /* ipAddressType_undo */

/*---------------------------------------------------------------------
 * IP-MIB::ipAddressEntry.ipAddressStatus
 * ipAddressStatus is subid 7 of ipAddressEntry.
 * Its status is Current, and its access level is Create.
 * OID: .1.3.6.1.2.1.4.34.1.7
 * Description:
The status of the address, describing if the address can be
            used for communication.


            In the absence of other information, an IPv4 address is
            always preferred(1).
 *
 * Attributes:
 *   accessible 1     isscalar 0     enums  1      hasdefval 1
 *   readable   1     iscolumn 1     ranges 0      hashint   0
 *   settable   1
 *   defval: preferred
 *
 * Enum range: 5/8. Values:  preferred(1), invalid(3), inaccessible(4), unknown(5), tentative(6), duplicate(7)
 *
 * Its syntax is IpAddressStatusTC (based on perltype INTEGER)
 * The net-snmp type is ASN_INTEGER. The C type decl is long (u_long)
 */
/**
 * Check that the proposed new value is potentially valid.
 *
 * @param rowreq_ctx
 *        Pointer to the row request context.
 * @param ipAddressStatus_val
 *        A long containing the new value.
 *
 * @retval MFD_SUCCESS        : incoming value is legal
 * @retval MFD_NOT_VALID_NOW  : incoming value is not valid now
 * @retval MFD_NOT_VALID_EVER : incoming value is never valid
 *
 * This is the place to check for requirements that are not
 * expressed in the mib syntax (for example, a requirement that
 * is detailed in the description for an object).
 *
 * You should check that the requested change between the undo value and the
 * new value is legal (ie, the transistion from one value to another
 * is legal).
 *      
 *@note
 * This check is only to determine if the new value
 * is \b potentially valid. This is the first check of many, and
 * is one of the simplest ones.
 * 
 *@note
 * this is not the place to do any checks for values
 * which depend on some other value in the mib. Those
 * types of checks should be done in the
 * ipAddressTable_check_dependencies() function.
 *
 * The following checks have already been done for you:
 *    The syntax is ASN_INTEGER
 *    The value is one of  preferred(1), invalid(3), inaccessible(4), unknown(5), tentative(6), duplicate(7)
 *
 * If there a no other checks you need to do, simply return MFD_SUCCESS.
 *
 */
int
ipAddressStatus_check_value(ipAddressTable_rowreq_ctx * rowreq_ctx,
                            u_long ipAddressStatus_val)
{
    DEBUGMSGTL(("verbose:ipAddressTable:ipAddressStatus_check_value",
                "called\n"));

    /** should never get a NULL pointer */
    netsnmp_assert(NULL != rowreq_ctx);

    /*
     * TODO:441:o: |-> Check for valid ipAddressStatus value.
     *
     * nothing but preferred supported yet
     */
    if (IPADDRESSSTATUSTC_PREFERRED != ipAddressStatus_val)
        return MFD_NOT_VALID_EVER;

    return MFD_SUCCESS;         /* ipAddressStatus value not illegal */
}                               /* ipAddressStatus_check_value */

/**
 * Save old value information
 *
 * @param rowreq_ctx
 *        Pointer to the table context (ipAddressTable_rowreq_ctx)
 *
 * @retval MFD_SUCCESS : success
 * @retval MFD_ERROR   : error. set will fail.
 *
 * This function will be called after the table level undo setup function
 * ipAddressTable_undo_setup has been called.
 *
 *@note
 * this function will only be called if a new value is set for this column.
 *
 * If there is any setup specific to a particular column (e.g. allocating
 * memory for a string), you should do that setup in this function, so it
 * won't be done unless it is necessary.
 */
int
ipAddressStatus_undo_setup(ipAddressTable_rowreq_ctx * rowreq_ctx)
{
    DEBUGMSGTL(("verbose:ipAddressTable:ipAddressStatus_undo_setup",
                "called\n"));

    /** should never get a NULL pointer */
    netsnmp_assert(NULL != rowreq_ctx);

    /*
     * TODO:455:o: |-> Setup ipAddressStatus undo.
     */
    /*
     * handled in ipAddressTable_undo_setup
     */

    return MFD_SUCCESS;
}                               /* ipAddressStatus_undo_setup */

/**
 * Set the new value.
 *
 * @param rowreq_ctx
 *        Pointer to the users context. You should know how to
 *        manipulate the value from this object.
 * @param ipAddressStatus_val
 *        A long containing the new value.
 */
int
ipAddressStatus_set(ipAddressTable_rowreq_ctx * rowreq_ctx,
                    u_long ipAddressStatus_val)
{

    DEBUGMSGTL(("verbose:ipAddressTable:ipAddressStatus_set", "called\n"));

    /** should never get a NULL pointer */
    netsnmp_assert(NULL != rowreq_ctx);

    /*
     * TODO:461:M: |-> Set ipAddressStatus value.
     * set ipAddressStatus value in rowreq_ctx->data
     */
    rowreq_ctx->data->ia_status = ipAddressStatus_val;

    return MFD_SUCCESS;
}                               /* ipAddressStatus_set */

/**
 * undo the previous set.
 *
 * @param rowreq_ctx
 *        Pointer to the users context.
 */
int
ipAddressStatus_undo(ipAddressTable_rowreq_ctx * rowreq_ctx)
{

    DEBUGMSGTL(("verbose:ipAddressTable:ipAddressStatus_undo",
                "called\n"));

    netsnmp_assert(NULL != rowreq_ctx);

    /*
     * TODO:456:o: |-> Clean up ipAddressStatus undo.
     */
    /*
     * copy ipAddressStatus data
     * set rowreq_ctx->data->ipAddressStatus from rowreq_ctx->undo->ipAddressStatus
     */
    rowreq_ctx->data->ia_status = rowreq_ctx->undo->ia_status;

    return MFD_SUCCESS;
}                               /* ipAddressStatus_undo */

/*---------------------------------------------------------------------
 * IP-MIB::ipAddressEntry.ipAddressRowStatus
 * ipAddressRowStatus is subid 10 of ipAddressEntry.
 * Its status is Current, and its access level is Create.
 * OID: .1.3.6.1.2.1.4.34.1.10
 * Description:
The status of this conceptual row.


            The RowStatus TC requires that this DESCRIPTION clause
            states under which circumstances other objects in this row
            can be modified.  The value of this object has no effect on
            whether other objects in this conceptual row can be
            modified.


            A conceptual row can not be made active until the
            ipAddressIfIndex has been set to a valid index.  
 *
 * Attributes:
 *   accessible 1     isscalar 0     enums  1      hasdefval 0
 *   readable   1     iscolumn 1     ranges 0      hashint   0
 *   settable   1
 *
 * Enum range: 3/8. Values:  active(1), notInService(2), notReady(3), createAndGo(4), createAndWait(5), destroy(6)
 *
 * Its syntax is RowStatus (based on perltype INTEGER)
 * The net-snmp type is ASN_INTEGER. The C type decl is long (u_long)
 */
/**
 * Check that the proposed new value is potentially valid.
 *
 * @param rowreq_ctx
 *        Pointer to the row request context.
 * @param ipAddressRowStatus_val
 *        A long containing the new value.
 *
 * @retval MFD_SUCCESS        : incoming value is legal
 * @retval MFD_NOT_VALID_NOW  : incoming value is not valid now
 * @retval MFD_NOT_VALID_EVER : incoming value is never valid
 *
 * This is the place to check for requirements that are not
 * expressed in the mib syntax (for example, a requirement that
 * is detailed in the description for an object).
 *
 * You should check that the requested change between the undo value and the
 * new value is legal (ie, the transistion from one value to another
 * is legal).
 *      
 *@note
 * This check is only to determine if the new value
 * is \b potentially valid. This is the first check of many, and
 * is one of the simplest ones.
 * 
 *@note
 * this is not the place to do any checks for values
 * which depend on some other value in the mib. Those
 * types of checks should be done in the
 * ipAddressTable_check_dependencies() function.
 *
 * The following checks have already been done for you:
 *    The syntax is ASN_INTEGER
 *    The value is one of  active(1), notInService(2), notReady(3), createAndGo(4), createAndWait(5), destroy(6)
 *
 * If there a no other checks you need to do, simply return MFD_SUCCESS.
 *
 */
int
ipAddressRowStatus_check_value(ipAddressTable_rowreq_ctx * rowreq_ctx,
                               u_long ipAddressRowStatus_val)
{
    int             rc;

    DEBUGMSGTL(("verbose:ipAddressTable:ipAddressRowStatus_check_value",
                "called\n"));

    /** should never get a NULL pointer */
    netsnmp_assert(NULL != rowreq_ctx);

    /*
     * TODO:441:o: |-> Check for valid ipAddressRowStatus value.
     *
     * don't support createAndWait
     * check for valid RowStatus transition (old, new)
     */
    if (ROWSTATUS_CREATEANDWAIT == ipAddressRowStatus_val) {
        DEBUGMSGTL(("ipAddressTable", "createAndWait not supported\n"));
        return MFD_NOT_VALID_EVER;
    }

    rc = check_rowstatus_transition(rowreq_ctx->ipAddressRowStatus,
                                    ipAddressRowStatus_val);
    if (MFD_SUCCESS != rc) {
        DEBUGMSGTL(("ipAddressTable", "row status transition from %d to %d\n",
                    rowreq_ctx->ipAddressRowStatus, ipAddressRowStatus_val));
        return rc;
    }

    return MFD_SUCCESS;         /* ipAddressRowStatus value not illegal */
}                               /* ipAddressRowStatus_check_value */

/**
 * Save old value information
 *
 * @param rowreq_ctx
 *        Pointer to the table context (ipAddressTable_rowreq_ctx)
 *
 * @retval MFD_SUCCESS : success
 * @retval MFD_ERROR   : error. set will fail.
 *
 * This function will be called after the table level undo setup function
 * ipAddressTable_undo_setup has been called.
 *
 *@note
 * this function will only be called if a new value is set for this column.
 *
 * If there is any setup specific to a particular column (e.g. allocating
 * memory for a string), you should do that setup in this function, so it
 * won't be done unless it is necessary.
 */
int
ipAddressRowStatus_undo_setup(ipAddressTable_rowreq_ctx * rowreq_ctx)
{
    DEBUGMSGTL(("verbose:ipAddressTable:ipAddressRowStatus_undo_setup",
                "called\n"));

    /** should never get a NULL pointer */
    netsnmp_assert(NULL != rowreq_ctx);

    /*
     * TODO:455:o: |-> Setup ipAddressRowStatus undo.
     */
    /*
     * handled in ipAddressTable_undo_setup
     */

    return MFD_SUCCESS;
}                               /* ipAddressRowStatus_undo_setup */

/**
 * Set the new value.
 *
 * @param rowreq_ctx
 *        Pointer to the users context. You should know how to
 *        manipulate the value from this object.
 * @param ipAddressRowStatus_val
 *        A long containing the new value.
 */
int
ipAddressRowStatus_set(ipAddressTable_rowreq_ctx * rowreq_ctx,
                       u_long ipAddressRowStatus_val)
{

    DEBUGMSGTL(("verbose:ipAddressTable:ipAddressRowStatus_set",
                "called\n"));

    /** should never get a NULL pointer */
    netsnmp_assert(NULL != rowreq_ctx);

    /*
     * TODO:461:M: |-> Set ipAddressRowStatus value.
     * set ipAddressRowStatus value in rowreq_ctx->data
     */
    rowreq_ctx->ipAddressRowStatus = ipAddressRowStatus_val;

    return MFD_SUCCESS;
}                               /* ipAddressRowStatus_set */

/**
 * undo the previous set.
 *
 * @param rowreq_ctx
 *        Pointer to the users context.
 */
int
ipAddressRowStatus_undo(ipAddressTable_rowreq_ctx * rowreq_ctx)
{

    DEBUGMSGTL(("verbose:ipAddressTable:ipAddressRowStatus_undo",
                "called\n"));

    netsnmp_assert(NULL != rowreq_ctx);

    /*
     * TODO:456:o: |-> Clean up ipAddressRowStatus undo.
     */
    /*
     * copy ipAddressRowStatus data
     * set rowreq_ctx->data->ipAddressRowStatus from rowreq_ctx->undo->ipAddressRowStatus
     */
    rowreq_ctx->ipAddressRowStatus = rowreq_ctx->ipAddressRowStatus_undo;

    return MFD_SUCCESS;
}                               /* ipAddressRowStatus_undo */

/*---------------------------------------------------------------------
 * IP-MIB::ipAddressEntry.ipAddressStorageType
 * ipAddressStorageType is subid 11 of ipAddressEntry.
 * Its status is Current, and its access level is Create.
 * OID: .1.3.6.1.2.1.4.34.1.11
 * Description:
The storage type for this conceptual row.  If this object
            has a value of 'permanent' then no other objects are
            required to be able to be modified.
 *
 * Attributes:
 *   accessible 1     isscalar 0     enums  1      hasdefval 1
 *   readable   1     iscolumn 1     ranges 0      hashint   0
 *   settable   1
 *   defval: volatile
 *
 * Enum range: 4/8. Values:  other(1), volatile(2), nonVolatile(3), permanent(4), readOnly(5)
 *
 * Its syntax is StorageType (based on perltype INTEGER)
 * The net-snmp type is ASN_INTEGER. The C type decl is long (u_long)
 */
/**
 * Check that the proposed new value is potentially valid.
 *
 * @param rowreq_ctx
 *        Pointer to the row request context.
 * @param ipAddressStorageType_val
 *        A long containing the new value.
 *
 * @retval MFD_SUCCESS        : incoming value is legal
 * @retval MFD_NOT_VALID_NOW  : incoming value is not valid now
 * @retval MFD_NOT_VALID_EVER : incoming value is never valid
 *
 * This is the place to check for requirements that are not
 * expressed in the mib syntax (for example, a requirement that
 * is detailed in the description for an object).
 *
 * You should check that the requested change between the undo value and the
 * new value is legal (ie, the transistion from one value to another
 * is legal).
 *      
 *@note
 * This check is only to determine if the new value
 * is \b potentially valid. This is the first check of many, and
 * is one of the simplest ones.
 * 
 *@note
 * this is not the place to do any checks for values
 * which depend on some other value in the mib. Those
 * types of checks should be done in the
 * ipAddressTable_check_dependencies() function.
 *
 * The following checks have already been done for you:
 *    The syntax is ASN_INTEGER
 *    The value is one of  other(1), volatile(2), nonVolatile(3), permanent(4), readOnly(5)
 *
 * If there a no other checks you need to do, simply return MFD_SUCCESS.
 *
 */
int
ipAddressStorageType_check_value(ipAddressTable_rowreq_ctx * rowreq_ctx,
                                 u_long ipAddressStorageType_val)
{
    int             rc;

    DEBUGMSGTL(("verbose:ipAddressTable:ipAddressStorageType_check_value",
                "called\n"));

    /** should never get a NULL pointer */
    netsnmp_assert(NULL != rowreq_ctx);

    /*
     * TODO:441:o: |-> Check for valid ipAddressStorageType value.
     */
    /*
     * since I don't know how the various operating systems
     * deal with ip addresses, and whether or not changes will
     * be saved on reboot, so don't allow rows to be set to anything
     * but volatile. I'd prefer other, but since the default for
     * new rows, per the mib, is volatile...
     *
     * If some industrious soul would like
     * non-volaltile support, the first would need to 
     *   add it in  the data access code for their os
     *   define a flag bit for volatile/permanent/readonly
     *   set the bit in data access
     *   copy the bit to a new var in the rowreq_ctx (see _add_new_entry)
     *        with a default of volatile (for os' w/out nonvolatile support)
     *   update this code to use new flag
     */
    if (STORAGETYPE_VOLATILE != ipAddressStorageType_val)
        return MFD_NOT_VALID_EVER;

    /*
     * check for valid StorageType transition (old, new)
     */
    rc = check_storage_transition(rowreq_ctx->data->ia_storagetype,
                                  ipAddressStorageType_val);
    if (MFD_SUCCESS != rc)
        return rc;

    return MFD_SUCCESS;         /* ipAddressStorageType value not illegal */
}                               /* ipAddressStorageType_check_value */

/**
 * Save old value information
 *
 * @param rowreq_ctx
 *        Pointer to the table context (ipAddressTable_rowreq_ctx)
 *
 * @retval MFD_SUCCESS : success
 * @retval MFD_ERROR   : error. set will fail.
 *
 * This function will be called after the table level undo setup function
 * ipAddressTable_undo_setup has been called.
 *
 *@note
 * this function will only be called if a new value is set for this column.
 *
 * If there is any setup specific to a particular column (e.g. allocating
 * memory for a string), you should do that setup in this function, so it
 * won't be done unless it is necessary.
 */
int
ipAddressStorageType_undo_setup(ipAddressTable_rowreq_ctx * rowreq_ctx)
{
    DEBUGMSGTL(("verbose:ipAddressTable:ipAddressStorageType_undo_setup",
                "called\n"));

    /** should never get a NULL pointer */
    netsnmp_assert(NULL != rowreq_ctx);

    /*
     * TODO:455:o: |-> Setup ipAddressStorageType undo.
     */
    /*
     * handled in ipAddressTable_undo_setup
     */

    return MFD_SUCCESS;
}                               /* ipAddressStorageType_undo_setup */

/**
 * Set the new value.
 *
 * @param rowreq_ctx
 *        Pointer to the users context. You should know how to
 *        manipulate the value from this object.
 * @param ipAddressStorageType_val
 *        A long containing the new value.
 */
int
ipAddressStorageType_set(ipAddressTable_rowreq_ctx * rowreq_ctx,
                         u_long ipAddressStorageType_val)
{

    DEBUGMSGTL(("verbose:ipAddressTable:ipAddressStorageType_set",
                "called\n"));

    /** should never get a NULL pointer */
    netsnmp_assert(NULL != rowreq_ctx);

    /*
     * TODO:461:M: |-> Set ipAddressStorageType value.
     * set ipAddressStorageType value in rowreq_ctx->data
     */
    rowreq_ctx->data->ia_storagetype = ipAddressStorageType_val;

    return MFD_SUCCESS;
}                               /* ipAddressStorageType_set */

/**
 * undo the previous set.
 *
 * @param rowreq_ctx
 *        Pointer to the users context.
 */
int
ipAddressStorageType_undo(ipAddressTable_rowreq_ctx * rowreq_ctx)
{

    DEBUGMSGTL(("verbose:ipAddressTable:ipAddressStorageType_undo",
                "called\n"));

    netsnmp_assert(NULL != rowreq_ctx);

    /*
     * TODO:456:o: |-> Clean up ipAddressStorageType undo.
     */
    /*
     * copy ipAddressStorageType data
     * set rowreq_ctx->data->ipAddressStorageType from rowreq_ctx->undo->ipAddressStorageType
     */
    rowreq_ctx->data->ia_storagetype = rowreq_ctx->undo->ia_storagetype;

    return MFD_SUCCESS;
}                               /* ipAddressStorageType_undo */

/**
 * check dependencies
 *
 * This is useful for for tables which have dependencies between columns
 * (or rows, or tables). For example, two columns allocating a percentage
 * of something add up 100%.
 *
 * Should you need different behavior depending on which columns were
 * set, rowreq_ctx->column_set_flags will indicate which writeable columns were
 * set. The definitions for the FLAG_* bits can be found in
 * ipAddressTable.h.
 * A new row will have the MFD_ROW_CREATED bit set in rowreq_flags.
 *
 * @retval MFD_SUCCESS all the changes to the row are legal
 * @retval MFD_ERROR   one or more changes are not legal
 *
 * (see README-table-ipAddressTable if you don't have dependencies)
 */
int
ipAddressTable_check_dependencies(ipAddressTable_rowreq_ctx * rowreq_ctx)
{
    int             rc = MFD_SUCCESS;

    DEBUGMSGTL(("internal:ipAddressTable:ipAddressTable_check_dependencies", "called\n"));

    netsnmp_assert(NULL != rowreq_ctx);

    /*
     * TODO:470:o: Check ipAddressTable row dependencies.
     * check that all new value are legal and consistent with each other
     */
    /*
     * check RowStatus dependencies
     */
    if (rowreq_ctx->column_set_flags & FLAG_IPADDRESSROWSTATUS) {
        /*
         * row creation requirements
         */
        if (rowreq_ctx->rowreq_flags & MFD_ROW_CREATED) {
            if (ROWSTATUS_CREATEANDGO == rowreq_ctx->ipAddressRowStatus) {
                if (rowreq_ctx->column_set_flags !=
                    IPADDRESSTABLE_REQUIRED_COLS) {
                    DEBUGMSGTL(("ipAddressTable",
                                "required columns missing\n"));
                    return MFD_CANNOT_CREATE_NOW;
                }
            }
        } /* row creation */
        else {
            /*
             * row change requirements
             */
            /*
             * don't allow a destroy if any other value was changed, since
             * that might call data access routines with bad info.
             *
             * you may or may not require the row be notInService before it
             * can be destroyed.
             */
            if (ROWSTATUS_DESTROY == rowreq_ctx->ipAddressRowStatus) {
                if (rowreq_ctx->
                    column_set_flags & ~FLAG_IPADDRESSROWSTATUS) {
                    DEBUGMSGTL(("ipAddressTable",
                                "destroy must be only varbind for row\n"));
                    return MFD_NOT_VALID_NOW;
                }
            }                   /* row destroy */
        }                       /* row change */
    } else {
        /*
         * must have row status to create a row
         */
        if (rowreq_ctx->rowreq_flags & MFD_ROW_CREATED) {
            DEBUGMSGTL(("ipAddressTable",
                        "must use RowStatus to create rows\n"));
            return MFD_CANNOT_CREATE_NOW;
        }
    }                           /* row status not set */

    return rc;
}                               /* ipAddressTable_check_dependencies */

/** @} */
/** @{ */
