/*      $NetBSD: snmp.c,v 1.11 2003/05/16 18:10:38 itojun Exp $ */

/*
* Copyright (c) 1992, 2001 Xerox Corporation.  All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither name of the Xerox, PARC, nor the names of its contributors may be used
* to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE XEROX CORPORATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "defs.h"
#include <netinet/in_var.h>
#include "snmp.h"
#include "snmplib/asn1.h"
#include "snmplib/party.h"
#include "snmplib/snmp_impl.h"
#define MROUTED
#include "snmpd/snmp_vars.h"

   in_port_t dest_port = 0;
   int sdlen = 0;

struct addrCache {
   u_long addr;
   int status;
#define UNUSED 0
#define USED   1
#define OLD 2
};

static struct addrCache addrCache[10];

/*
* Initialize the SNMP part of mrouted
*/
int /* returns: 0 on success, true on error */
snmp_init(dest_port)
   in_port_t dest_port;
{
  u_long myaddr;
  int ret;
  struct partyEntry *pp;
  struct sockaddr_in  me;
  int index, sd, portlist[32];

  init_snmp();
  /* init_mib(); why was this here? */
   if (read_party_database("/etc/party.conf") > 0){
  fprintf(stderr, "Couldn't read party database from /etc/party.conf\n");
  exit(0);
   }
   if (read_context_database("/etc/context.conf") > 0){
  fprintf(stderr, "Couldn't read context database from /etc/context.conf\n");
  exit(0);
   }
   if (read_acl_database("/etc/acl.conf") > 0){
  fprintf(stderr, "Couldn't read acl database from /etc/acl.conf\n");
  exit(0);
   }
   if (read_view_database("/etc/view.conf") > 0){
  fprintf(stderr, "Couldn't read view database from /etc/view.conf\n");
  exit(0);
   }

   myaddr = get_myaddr();
   if (ret = agent_party_init(myaddr, ".1.3.6.1")){
  if (ret == 1){
      fprintf(stderr, "Conflict found with initial noAuth/noPriv parties... continuing\n");
  } else if (ret == -1){
      fprintf(stderr, "Error installing initial noAuth/noPriv parties, exiting\n");
      exit(1);
  } else {
      fprintf(stderr, "Unknown error, exiting\n");
      exit(2);
  }
   }

   printf("Opening port(s): ");
   fflush(stdout);
   party_scanInit();
   for(pp = party_scanNext(); pp; pp = party_scanNext()){
  if ((pp->partyTDomain != DOMAINSNMPUDP)
      || bcmp((char *)&myaddr, pp->partyTAddress, 4))
      continue;  /* don't listen for non-local parties */

  dest_port = 0;
  bcopy(pp->partyTAddress + 4, &dest_port, 2);
  for(index = 0; index < sdlen; index++)
      if (dest_port == portlist[index])
     break;
  if (index < sdlen)  /* found a hit before the end of the list */
      continue;
  printf("%u ", dest_port);
  fflush(stdout);
  /* Set up connections */
  sd = socket(AF_INET, SOCK_DGRAM, 0);
  if (sd < 0){
      perror("socket");
      return 1;
  }
  memset(&me, 0, sizeof(me));
  me.sin_family = AF_INET;
  me.sin_addr.s_addr = INADDR_ANY;
  /* already in network byte order (I think) */
  me.sin_port = dest_port;
  if (bind(sd, (struct sockaddr *)&me, sizeof(me)) != 0){
      perror("bind");
      return 2;
  }
  register_input_handler(sd, snmp_read_packet);
  portlist[sdlen] = dest_port;
  if (++sdlen == 32){
      printf("No more sockets... ignoring rest of file\n");
      break;
  }
   }
   printf("\n");
   bzero((char *)addrCache, sizeof(addrCache));
}

/*
* Place an IP address into an OID starting at element n
*/
void
put_address(name, addr, n)
  oid   *name;
  u_long addr;
  int n;
{
  int i;

  for (i=n+3; i>=n+0; i--) {
     name[i] = addr & 0xFF;
     addr >>= 8;
  }
}

/* Get an IP address from an OID starting at element n */
int
get_address(name, length, addr, n)
  oid   *name;
  int    length;
  u_long *addr;
  int n;
{
  int i;
  int ok = 1;

  (*addr) = 0;

  if (length < n+4)
     return 0;

  for (i=n; i<n+4; i++) {
     (*addr) <<= 8;
     if (i >= length)
         ok = 0;
     else
        (*addr) |= name[i];
  }
  return ok;
}

/*
* Implements scalar objects from DVMRP and Multicast MIBs
*/
u_char *
o_scalar(vp, name, length, exact, var_len, write_method)
   struct variable *vp;   /* IN - pointer to variable entry that points here */
   oid *name;      /* IN/OUT - input name requested, output name found */
   int *length;    /* IN/OUT - length of input and output oid's */
   int                 exact;      /* IN - TRUE if an exact match was requested. */
   int                 *var_len;   /* OUT - length of variable or 0 if function returned. */
   int                 (**write_method)(); /* OUT - pointer to function to set variable, otherwise 0 */
{
   int result;

   *write_method = 0;
   result = compare(name, *length, vp->name, (int)vp->namelen);
   if ((exact && (result != 0)) || (!exact && (result >= 0)))
  return NULL;

       bcopy((char *)vp->name, (char *)name,
    (int)vp->namelen * sizeof(oid));
       *length = vp->namelen;
       *var_len = sizeof(long);

   switch (vp->magic) {

   case ipMRouteEnable:
      long_return = 1;
      return (u_char *) &long_return;

   case dvmrpVersion: {
      static char buff[15];

      snprintf(buff, sizeof(buff), "mrouted%d.%d", PROTOCOL_VERSION,
              MROUTED_VERSION);
      *var_len = strlen(buff);
      return (u_char *)buff;
   }

   case dvmrpGenerationId:
      long_return = dvmrp_genid;
      return (u_char *) &long_return;

   default:
      ERROR("");
   }
   return NULL;
}

/*
* Find if a specific scoped boundary exists on a Vif
*/
struct vif_acl *
find_boundary(vifi, addr, mask)
  vifi_t vifi;
  u_long addr;
  u_long mask;
{
  struct vif_acl *n;

  for (n = uvifs[vifi].uv_acl; n != NULL; n = n->acl_next) {
     if (addr == n->acl_addr && mask==n->acl_mask)
        return n;
  }
  return NULL;
}

/*
* Find the lowest boundary >= (V,A,M) spec
*/
struct vif_acl *
next_boundary(vifi, addr, mask)
  vifi_t *vifi;
  u_long  addr;
  u_long  mask;
{
  struct vif_acl *bestn, *n;
  int  i;

  for (i = *vifi; i < numvifs; i++) {
     bestn = NULL;
     for (n = uvifs[i].uv_acl; n; n=n->acl_next) {
        if ((i > *vifi || n->acl_addr > addr
          || (n->acl_addr == addr && n->acl_mask >= mask))
         && (!bestn || n->acl_addr < bestn->acl_addr
          || (n->acl_addr==bestn->acl_addr && n->acl_mask<bestn->acl_mask)))
           bestn = n;
     }
     if (bestn) {
        *vifi = i;
        return bestn;
     }
  }
  return NULL;
}

/*
* Implements the Boundary Table portion of the DVMRP MIB
*/
u_char *
o_dvmrpBoundaryTable(vp, name, length, exact, var_len, write_method)
   struct variable *vp;   /* IN - pointer to variable entry that points here */
   oid *name;      /* IN/OUT - input name requested, output name found */
   int *length;    /* IN/OUT - length of input and output oid's */
   int                 exact;      /* IN - TRUE if an exact match was requested. */
   int                 *var_len;   /* OUT - length of variable or 0 if function returned. */
   int                 (**write_method)(); /* OUT - pointer to function to set variable, otherwise 0 */
{
   vifi_t     vifi;
   u_long         addr, mask;
   struct vif_acl *bound;
   oid        newname[MAX_NAME_LEN];
   int        len;

   /* Copy name OID to new OID */
   bcopy((char *)vp->name, (char *)newname, (int)vp->namelen * sizeof(oid));

   if (exact) {
           if (*length != vp->namelen + 9)
               return NULL;

     if ((vifi = name[vp->namelen]) >= numvifs)
     return NULL;

     if (!get_address(name, *length, &addr, vp->namelen+1)
      || !get_address(name, *length, &mask, vp->namelen+5))
               return NULL;

     if (!(bound = find_boundary(vifi, addr, mask)))
               return NULL;

      bcopy((char *)name, (char *)newname, ((int)*length) * sizeof(oid));
        } else {
      len = *length;
      if (compare(name, *length, vp->name, vp->namelen) < 0)
         len = vp->namelen;

           if (len < vp->namelen + 9) { /* get first entry */

        if (len == vp->namelen) {
           vifi = addr = mask = 0;
        } else {
           vifi = name[vp->namelen];
           get_address(name, len, &addr, vp->namelen+1);
           get_address(name, len, &mask, vp->namelen+5);
        }

        bound = next_boundary(&vifi,addr,mask);
        if (!bound)
           return NULL;

               newname[vp->namelen] = vifi;
        put_address(newname, bound->acl_addr, vp->namelen+1);
        put_address(newname, bound->acl_mask, vp->namelen+5);
           } else {  /* get next entry given previous */
                  vifi = name[vp->namelen];
        get_address(name, *length, &addr, vp->namelen+1);
        get_address(name, *length, &mask, vp->namelen+5);

        if (!(bound = next_boundary(&vifi,addr,mask+1)))
           return NULL;

                  newname[vp->namelen] = vifi;
        put_address(newname, bound->acl_addr, vp->namelen+1);
        put_address(newname, bound->acl_mask, vp->namelen+5);
           }
   }

   /* Save new OID */
   *length = vp->namelen + 9;
   bcopy((char *)newname, (char *)name, ((int)*length) * sizeof(oid));
   *write_method = 0;
   *var_len = sizeof(long);

   switch (vp->magic) {

  case dvmrpBoundaryVifIndex:
      long_return = vifi;
      return (u_char *) &long_return;

   default:
      ERROR("");
   }
   return NULL;
}

/*
* Find the lowest neighbor >= (V,A) spec
*/
struct listaddr *
next_neighbor(vifi, addr)
  vifi_t *vifi;
  u_long  addr;
{
  struct listaddr *bestn, *n;
  int  i;

  for (i = *vifi; i < numvifs; i++) {
     bestn = NULL;
     for (n = uvifs[i].uv_neighbors; n; n=n->al_next) {
        if ((i > *vifi || n->al_addr >= addr)
         && (!bestn || n->al_addr < bestn->al_addr))
           bestn = n;
     }
     if (bestn) {
        *vifi = i;
        return bestn;
     }
  }
  return NULL;
}

/*
* Find a neighbor, if it exists off a given Vif
*/
struct listaddr *
find_neighbor(vifi, addr)
  vifi_t vifi;
  u_long addr;
{
  struct listaddr *n;

  for (n = uvifs[vifi].uv_neighbors; n != NULL; n = n->al_next) {
     if (addr == n->al_addr)
        return n;
  }
  return NULL;
}

u_char *
o_dvmrpNeighborTable(vp, name, length, exact, var_len, write_method)
   struct variable *vp;   /* IN - pointer to variable entry that points here */
   oid *name;      /* IN/OUT - input name requested, output name found */
   int *length;    /* IN/OUT - length of input and output oid's */
   int                 exact;      /* IN - TRUE if an exact match was requested. */
   int                 *var_len;   /* OUT - length of variable or 0 if function returned. */
   int                 (**write_method)(); /* OUT - pointer to function to set variable, otherwise 0 */
{
   vifi_t     vifi;
   u_long     addr, mask;
   struct listaddr *neighbor;
   oid        newname[MAX_NAME_LEN];
   int        len;

   /* Copy name OID to new OID */
   bcopy((char *)vp->name, (char *)newname, (int)vp->namelen * sizeof(oid));

   if (exact) {
           if (*length != vp->namelen + 5)
               return NULL;

     if ((vifi = name[vp->namelen]) >= numvifs)
     return NULL;

     if (!get_address(name, *length, &addr, vp->namelen+1))
               return NULL;

     if (!(neighbor = find_neighbor(vifi, addr)))
               return NULL;

      bcopy((char *)name, (char *)newname, ((int)*length) * sizeof(oid));
        } else {
      len = *length;
      if (compare(name, *length, vp->name, vp->namelen) < 0)
         len = vp->namelen;

           if (len < vp->namelen + 5) { /* get first entry */

        if (len == vp->namelen) {
           vifi = addr = 0;
        } else {
           vifi = name[vp->namelen];
           get_address(name, len, &addr, vp->namelen+1);
        }

        neighbor = next_neighbor(&vifi,addr);
        if (!neighbor)
           return NULL;

               newname[vp->namelen] = vifi;
        put_address(newname, neighbor->al_addr, vp->namelen+1);
           } else {  /* get next entry given previous */
                  vifi = name[vp->namelen];
        get_address(name, *length, &addr, vp->namelen+1);

        if (!(neighbor = next_neighbor(&vifi,addr+1)))
           return NULL;

                  newname[vp->namelen] = vifi;
        put_address(newname, neighbor->al_addr, vp->namelen+1);
           }
   }

   /* Save new OID */
   *length = vp->namelen + 5;
   bcopy((char *)newname, (char *)name, ((int)*length) * sizeof(oid));
   *write_method = 0;
   *var_len = sizeof(long);

   switch (vp->magic) {

  case dvmrpNeighborUpTime: {
      time_t currtime;
      time(&currtime);
      long_return = (currtime - neighbor->al_ctime)*100;
      return (u_char *) &long_return;
  }

  case dvmrpNeighborExpiryTime:
      long_return = (NEIGHBOR_EXPIRE_TIME - neighbor->al_timer
       + secs_remaining_offset()) * 100;
      return (u_char *) &long_return;

  case dvmrpNeighborVersion: {
      static char buff[15];

      snprintf(buff, sizeof(buff), "%d.%d", neighbor->al_pv, neighbor->al_mv);
      *var_len = strlen(buff);
      return (u_char *)buff;
  }

  case dvmrpNeighborGenerationId:
      long_return = neighbor->al_genid;
      return (u_char *) &long_return;

   default:
      ERROR("");
   }
   return NULL;
}

/* Look up ifIndex given uvifs[ifnum].uv_lcl_addr */
struct in_ifaddr *        /* returns: in_ifaddr structure, or null on error */
ipaddr_to_ifindex(ipaddr, ifIndex)
  u_long ipaddr;
  int   *ifIndex;
{
   int interface;
static struct in_ifaddr in_ifaddr;

   Interface_Scan_Init();
   for (;;) {
      if (Interface_Scan_Next(&interface, (char *)0, NULL, &in_ifaddr) == 0)
         return NULL;

      if (((struct sockaddr_in *) &(in_ifaddr.ia_addr))->sin_addr.s_addr
       == ipaddr) {
         *ifIndex = interface;
         return &in_ifaddr;
      }
   }
}

/*
* Find if a specific scoped boundary exists on a Vif
*/
struct listaddr *
find_cache(grp, vifi)
  u_long grp;
  vifi_t vifi;
{
  struct listaddr *n;

  for (n = uvifs[vifi].uv_groups; n != NULL; n = n->al_next) {
     if (grp == n->al_addr)
        return n;
  }
  return NULL;
}

/*
* Find the next group cache entry >= (A,V) spec
*/
struct listaddr *
next_cache(addr, vifi)
  u_long  addr;
  vifi_t *vifi;
{
  struct listaddr *bestn=NULL, *n;
  int  i, besti;

  /* Step through all entries looking for the next one */
  for (i = 0; i < numvifs; i++) {
     for (n = uvifs[i].uv_groups; n; n=n->al_next) {
        if ((n->al_addr > addr || (n->al_addr == addr && i >= *vifi))
         && (!bestn || n->al_addr < bestn->al_addr
          || (n->al_addr == bestn->al_addr && i < besti))) {
           bestn = n;
           besti = i;
        }
     }
  }

  if (bestn) {
     *vifi = besti;
     return bestn;
  }
  return NULL;
}

/*
* Implements the IGMP Cache Table portion of the IGMP MIB
*/
u_char *
o_igmpCacheTable(vp, name, length, exact, var_len, write_method)
   struct variable *vp;   /* IN - pointer to variable entry that points here */
   oid *name;      /* IN/OUT - input name requested, output name found */
   int *length;    /* IN/OUT - length of input and output oid's */
   int                 exact;      /* IN - TRUE if an exact match was requested. */
   int                 *var_len;   /* OUT - length of variable or 0 if function returned. */
   int                 (**write_method)(); /* OUT - pointer to function to set variable, otherwise 0 */
{
   vifi_t     vifi;
   u_long     grp;
   int       ifIndex;
   struct listaddr *cache;
   oid        newname[MAX_NAME_LEN];
   int        len;
   struct in_ifaddr *in_ifaddr;
   struct in_multi   in_multi, *inm;

   /* Copy name OID to new OID */
   bcopy((char *)vp->name, (char *)newname, (int)vp->namelen * sizeof(oid));

   if (exact) {
           if (*length != vp->namelen + 5)
               return NULL;

     if ((vifi = name[vp->namelen+4]) >= numvifs)
     return NULL;

     if (!get_address(name, *length, &grp, vp->namelen))
               return NULL;

     if (!(cache = find_cache(grp, vifi)))
               return NULL;

      bcopy((char *)name, (char *)newname, ((int)*length) * sizeof(oid));
        } else {
      len = *length;
      if (compare(name, *length, vp->name, vp->namelen) < 0)
         len = vp->namelen;

           if (len < vp->namelen + 5) { /* get first entry */

        if (len == vp->namelen) {
           vifi = grp = 0;
        } else {
           get_address(name, len, &grp, vp->namelen);
           vifi = name[vp->namelen+4];
        }

        cache = next_cache(grp,&vifi);
        if (!cache)
           return NULL;

        put_address(newname, cache->al_addr, vp->namelen);
               newname[vp->namelen+4] = vifi;
           } else {  /* get next entry given previous */
        get_address(name, *length, &grp, vp->namelen);
                  vifi = name[vp->namelen+4]+1;

        if (!(cache = next_cache(grp,&vifi)))
           return NULL;

        put_address(newname, cache->al_addr, vp->namelen);
                  newname[vp->namelen+4] = vifi;
           }
   }

   /* Save new OID */
   *length = vp->namelen + 5;
   bcopy((char *)newname, (char *)name, ((int)*length) * sizeof(oid));
   *write_method = 0;
   *var_len = sizeof(long);

   /* Look up ifIndex given uvifs[vifi].uv_lcl_addr */
   in_ifaddr = ipaddr_to_ifindex(uvifs[vifi].uv_lcl_addr, &ifIndex);

   switch (vp->magic) {

  case igmpCacheSelf:
      inm = in_ifaddr->ia_multiaddrs;
      while (inm) {
         klookup( (int)inm, (char *)&in_multi, sizeof(in_multi));

         if (in_multi.inm_addr.s_addr == cache->al_addr) {
            long_return = 1; /* true */
            return (u_char *) &long_return;
         }

         inm = in_multi.inm_next;
      }
      long_return = 2; /* false */
      return (u_char *) &long_return;

  case igmpCacheLastReporter:
      return (u_char *) &cache->al_genid;

  case igmpCacheUpTime: {
     time_t currtime;
     time(&currtime);
     long_return = (currtime - cache->al_ctime)*100;
     return (u_char *) &long_return;
  }

  case igmpCacheExpiryTime:
      long_return = secs_remaining(cache->al_timerid)*100;
      return (u_char *) &long_return;

  case igmpCacheStatus:
      long_return = 1;
      return (u_char *) &long_return;

   default:
      ERROR("");
   }
   return NULL;
}

/*
* Implements the IGMP Interface Table portion of the IGMP MIB
*/
u_char *
o_igmpInterfaceTable(vp, name, length, exact, var_len, write_method)
   struct variable *vp;   /* IN - pointer to variable entry that points here */
   oid *name;      /* IN/OUT - input name requested, output name found */
   int *length;    /* IN/OUT - length of input and output oid's */
   int                 exact;      /* IN - TRUE if an exact match was requested. */
   int                 *var_len;   /* OUT - length of variable or 0 if function returned. */
   int                 (**write_method)(); /* OUT - pointer to function to set variable, otherwise 0 */
{
   oid                 newname[MAX_NAME_LEN];
   int ifnum;
   int result;
static struct sioc_vif_req v_req;

   /* Copy name OID to new OID */
   bcopy((char *)vp->name, (char *)newname, (int)vp->namelen * sizeof(oid));

   /* find "next" interface */
   for(ifnum = 0; ifnum < numvifs; ifnum++){
      if (!(uvifs[ifnum].uv_flags & VIFF_QUERIER))
          continue;
      newname[vp->namelen] = (oid)ifnum;
      result = compare(name, *length, newname, (int)vp->namelen + 1);
      if ((exact && (result == 0)) || (!exact && (result < 0)))
         break;
   }
   if (ifnum >= numvifs)
      return NULL;

   /* Save new OID */
   bcopy((char *)newname, (char *)name, ((int)vp->namelen + 1) * sizeof(oid));
   *length = vp->namelen + 1;
   *write_method = 0;
   *var_len = sizeof(long);

   switch (vp->magic){

       case igmpInterfaceQueryInterval:
               long_return = GROUP_QUERY_INTERVAL;
     return (u_char *) &long_return;

       case igmpInterfaceStatus:
               long_return = 1; /* active */
     return (u_char *) &long_return;

       default:
           ERROR("");
   }
   return NULL;
}

/*
* Given a virtual interface number, make sure we have the current
* kernel information for that Vif.
*/
refresh_vif(v_req, ifnum)
  struct sioc_vif_req *v_req;
  int ifnum;
{
  static   int lastq = -1;

  if (quantum!=lastq || v_req->vifi != ifnum) {
      lastq = quantum;
      v_req->vifi = ifnum;
      if (ioctl(igmp_socket, SIOCGETVIFCNT, (char *)v_req) < 0)
         v_req->icount = v_req->ocount = v_req->ibytes = v_req->obytes = 0;
  }
}

/*
* Implements the Multicast Routing Interface Table portion of the Multicast MIB
*/
u_char *
o_ipMRouteInterfaceTable(vp, name, length, exact, var_len, write_method)
   struct variable *vp;   /* IN - pointer to variable entry that points here */
   oid *name;      /* IN/OUT - input name requested, output name found */
   int *length;    /* IN/OUT - length of input and output oid's */
   int                 exact;      /* IN - TRUE if an exact match was requested. */
   int                 *var_len;   /* OUT - length of variable or 0 if function returned. */
   int                 (**write_method)(); /* OUT - pointer to function to set variable, otherwise 0 */
{
   oid                 newname[MAX_NAME_LEN];
   int ifnum;
   int result;
static struct sioc_vif_req v_req;

   /* Copy name OID to new OID */
   bcopy((char *)vp->name, (char *)newname, (int)vp->namelen * sizeof(oid));

   /* find "next" interface */
   for(ifnum = 0; ifnum < numvifs; ifnum++){
       newname[vp->namelen] = (oid)ifnum;
       result = compare(name, *length, newname, (int)vp->namelen + 1);
       if ((exact && (result == 0)) || (!exact && (result < 0)))
           break;
   }
   if (ifnum >= numvifs)
       return NULL;

   /* Save new OID */
   bcopy((char *)newname, (char *)name, ((int)vp->namelen + 1) * sizeof(oid));
   *length = vp->namelen + 1;
   *write_method = 0;
   *var_len = sizeof(long);

   switch (vp->magic){

  case ipMRouteInterfaceTtl:
      long_return = uvifs[ifnum].uv_threshold;
      return (u_char *) &long_return;

  case dvmrpVInterfaceType:
     if (uvifs[ifnum].uv_flags & VIFF_SRCRT)
        long_return = 2;
     else if (uvifs[ifnum].uv_flags & VIFF_TUNNEL)
        long_return = 1;
     else if (uvifs[ifnum].uv_flags & VIFF_QUERIER)
        long_return = 3;
     else                               /* SUBNET */
        long_return = 4;
     return (u_char *) &long_return;

  case dvmrpVInterfaceState:
     if (uvifs[ifnum].uv_flags & VIFF_DISABLED)
        long_return = 3;
     else if ((uvifs[ifnum].uv_flags & VIFF_DOWN)
      || ((uvifs[ifnum].uv_flags & VIFF_TUNNEL) && (uvifs[ifnum].uv_neighbors==NULL)))
        long_return = 2;
     else /* UP */
        long_return = 1;
     return (u_char *) &long_return;

  case dvmrpVInterfaceLocalAddress:
     return (u_char *) &uvifs[ifnum].uv_lcl_addr;

  case dvmrpVInterfaceRemoteAddress:
     return (u_char *) ((uvifs[ifnum].uv_flags & VIFF_TUNNEL) ?
        &uvifs[ifnum].uv_rmt_addr :
        &uvifs[ifnum].uv_subnet);

  case dvmrpVInterfaceRemoteSubnetMask:
     return (u_char *) &uvifs[ifnum].uv_subnetmask;

  case dvmrpVInterfaceMetric:
      long_return = uvifs[ifnum].uv_metric;
      return (u_char *) &long_return;

  case dvmrpVInterfaceRateLimit:
      long_return = uvifs[ifnum].uv_rate_limit;
      return (u_char *) &long_return;

  case dvmrpVInterfaceInPkts:
      refresh_vif(&v_req, ifnum);
      long_return = v_req.icount;
      return (u_char *) &long_return;

  case dvmrpVInterfaceOutPkts:
      refresh_vif(&v_req, ifnum);
      long_return = v_req.ocount;
      return (u_char *) &long_return;

  case dvmrpVInterfaceInOctets:
      refresh_vif(&v_req, ifnum);
      long_return = v_req.ibytes;
      return (u_char *) &long_return;

  case dvmrpVInterfaceOutOctets:
      refresh_vif(&v_req, ifnum);
      long_return = v_req.obytes;
      return (u_char *) &long_return;

       default:
           ERROR("");
   }
   return NULL;
}

/*
* Implements the DVMRP Route Table portion of the DVMRP MIB
*/
u_char *
o_dvmrpRouteTable(vp, name, length, exact, var_len, write_method)
   struct variable *vp;   /* IN - pointer to variable entry that points here */
   oid *name;      /* IN/OUT - input name requested, output name found */
   int *length;    /* IN/OUT - length of input and output oid's */
   int                 exact;      /* IN - TRUE if an exact match was requested. */
   int                 *var_len;   /* OUT - length of variable or 0 if function returned. */
   int                 (**write_method)(); /* OUT - pointer to function to set variable, otherwise 0 */
{
   u_long src, mask;
   oid        newname[MAX_NAME_LEN];
   int        len;
   struct rtentry *rt = NULL;

   /* Copy name OID to new OID */
   bcopy((char *)vp->name, (char *)newname, (int)vp->namelen * sizeof(oid));

   if (exact) {
           if (*length != vp->namelen + 8)
               return NULL;

     if (!get_address(name, *length, &src, vp->namelen)
      || !get_address(name, *length, &mask, vp->namelen+4))
               return NULL;

     if (!(rt = snmp_find_route(src, mask)))
               return NULL;

      bcopy((char *)name, (char *)newname, ((int)*length) * sizeof(oid));
        } else {
      len = *length;
      if (compare(name, *length, vp->name, vp->namelen) < 0)
         len = vp->namelen;

           if (len < vp->namelen + 8) { /* get first entry */

        if (len == vp->namelen) {
           src = mask = 0;
        } else {
           get_address(name, len, &src, vp->namelen);
           get_address(name, len, &mask, vp->namelen+4);
        }

        if (!next_route(&rt,src,mask)) /* Get first entry */
           return NULL;

        put_address(newname, rt->rt_origin    , vp->namelen);
        put_address(newname, rt->rt_originmask, vp->namelen+4);
           } else {  /* get next entry given previous */
        get_address(name, *length, &src,  vp->namelen);
        get_address(name, *length, &mask, vp->namelen+4);

        if (!next_route(&rt, src,mask))
           return NULL;

        put_address(newname, rt->rt_origin,     vp->namelen);
        put_address(newname, rt->rt_originmask, vp->namelen+4);
           }
   }

   /* Save new OID */
   *length = vp->namelen + 8;
   bcopy((char *)newname, (char *)name, ((int)*length) * sizeof(oid));
   *write_method = 0;
   *var_len = sizeof(long);

   switch (vp->magic) {

     case dvmrpRouteUpstreamNeighbor:
        return (u_char *) &rt->rt_gateway;

     case dvmrpRouteInVifIndex:
        long_return = rt->rt_parent;
        return (u_char *) &long_return;

     case dvmrpRouteMetric:
        long_return = rt->rt_metric;
        return (u_char *) &long_return;

     case dvmrpRouteExpiryTime:
        long_return = (ROUTE_EXPIRE_TIME - rt->rt_timer
         + secs_remaining_offset()) * 100;
        return (u_char *) &long_return;

   default:
      ERROR("");
   }
   return NULL;
}

/*
* Implements the DVMRP Routing Next Hop Table portion of the DVMRP MIB
*/
u_char *
o_dvmrpRouteNextHopTable(vp, name, length, exact, var_len, write_method)
   struct variable *vp;   /* IN - pointer to variable entry that points here */
   oid *name;      /* IN/OUT - input name requested, output name found */
   int *length;    /* IN/OUT - length of input and output oid's */
   int                 exact;      /* IN - TRUE if an exact match was requested. */
   int                 *var_len;   /* OUT - length of variable or 0 if function returned. */
   int                 (**write_method)(); /* OUT - pointer to function to set variable, otherwise 0 */
{
   u_long     src, mask;
   vifi_t     vifi;
   struct rtentry *rt = NULL;
   oid        newname[MAX_NAME_LEN];
   int        len;

   /* Copy name OID to new OID */
   bcopy((char *)vp->name, (char *)newname, (int)vp->namelen * sizeof(oid));

   if (exact) {
           if (*length != vp->namelen + 9)
               return NULL;

     if (!get_address(name, *length, &src, vp->namelen)
      || !get_address(name, *length, &mask, vp->namelen+4)
      || (!(rt=snmp_find_route(src,mask))))
               return NULL;

     vifi = name[vp->namelen+8];
     if (!(VIFM_ISSET(vifi, rt->rt_children)))
     return NULL;

      bcopy((char *)name, (char *)newname, ((int)*length) * sizeof(oid));
        } else {
      len = *length;
      if (compare(name, *length, vp->name, vp->namelen) < 0)
         len = vp->namelen;

           if (len < vp->namelen + 9) { /* get first entry */

        get_address(name, len, &src,  vp->namelen);
        get_address(name, len, &mask, vp->namelen+4);

        /* Find first child vif */
        vifi=0;
        if (!next_route_child(&rt, src, mask, &vifi))
           return NULL;

        put_address(newname, rt->rt_origin,     vp->namelen);
        put_address(newname, rt->rt_originmask, vp->namelen+4);
               newname[vp->namelen+8] = vifi;
           } else {  /* get next entry given previous */
                  vifi = name[vp->namelen+8] + 1;
        if (!get_address(name, *length, &src,  vp->namelen)
         || !get_address(name, *length, &mask, vp->namelen+4)
         || !next_route_child(&rt, src, mask, &vifi))
           return NULL;

        put_address(newname, rt->rt_origin,     vp->namelen);
        put_address(newname, rt->rt_originmask, vp->namelen+4);
                  newname[vp->namelen+8] = vifi;
           }
   }

   /* Save new OID */
   *length = vp->namelen + 9;
   bcopy((char *)newname, (char *)name, ((int)*length) * sizeof(oid));
   *write_method = 0;
   *var_len = sizeof(long);

   switch (vp->magic) {

   case dvmrpRouteNextHopType:
      long_return = (VIFM_ISSET(vifi, rt->rt_leaves))? 1 : 2;
      return (u_char *) &long_return;

   default:
      ERROR("");
   }
   return NULL;
}

/*
* Implements the IP Multicast Route Table portion of the Multicast MIB
*/
u_char *
o_ipMRouteTable(vp, name, length, exact, var_len, write_method)
   struct variable *vp;   /* IN - pointer to variable entry that points here */
   oid *name;      /* IN/OUT - input name requested, output name found */
   int *length;    /* IN/OUT - length of input and output oid's */
   int                 exact;      /* IN - TRUE if an exact match was requested. */
   int                 *var_len;   /* OUT - length of variable or 0 if function returned. */
   int                 (**write_method)(); /* OUT - pointer to function to set variable, otherwise 0 */
{
   u_long src, grp, mask;
   struct gtable *gt = NULL;
   struct stable *st = NULL;
static struct sioc_sg_req sg_req;
   oid        newname[MAX_NAME_LEN];
   int        len;

   /* Copy name OID to new OID */
   bcopy((char *)vp->name, (char *)newname, (int)vp->namelen * sizeof(oid));

   if (exact) {
           if (*length != vp->namelen + 12)
               return NULL;

     if (!get_address(name, *length, &grp,  vp->namelen)
      || !get_address(name, *length, &src,  vp->namelen+4)
      || !get_address(name, *length, &mask, vp->namelen+8)
      || (mask != 0xFFFFFFFF) /* we keep sources now, not subnets */
      || !(gt = find_grp(grp))
      || !(st = find_grp_src(gt,src)))
               return NULL;

      bcopy((char *)name, (char *)newname, ((int)*length) * sizeof(oid));
        } else {
      len = *length;
      if (compare(name, *length, vp->name, vp->namelen) < 0)
         len = vp->namelen;

           if (len < vp->namelen + 12) { /* get first entry */

        get_address(name, len, &grp,  vp->namelen);
        get_address(name, len, &src,  vp->namelen+4);
        get_address(name, len, &mask, vp->namelen+8);

        if (!next_grp_src_mask(&gt,&st,grp,src,mask)) /* Get first entry */
           return NULL;

        put_address(newname, gt->gt_mcastgrp, vp->namelen);
        put_address(newname, st->st_origin,   vp->namelen+4);
        put_address(newname, 0xFFFFFFFF,      vp->namelen+8);
           } else {  /* get next entry given previous */
        get_address(name, *length, &grp , vp->namelen);
        get_address(name, *length, &src , vp->namelen+4);
        get_address(name, *length, &mask, vp->namelen+8);

        if (!next_grp_src_mask(&gt, &st, grp,src,mask))
           return NULL;

        put_address(newname, gt->gt_mcastgrp, vp->namelen);
        put_address(newname, st->st_origin,   vp->namelen+4);
        put_address(newname, 0xFFFFFFFF,      vp->namelen+8);
           }
   }

   /* Save new OID */
   *length = vp->namelen + 12;
   bcopy((char *)newname, (char *)name, ((int)*length) * sizeof(oid));
   *write_method = 0;
   *var_len = sizeof(long);

   switch (vp->magic) {

     case ipMRouteUpstreamNeighbor:
        return (u_char *) &gt->gt_route->rt_gateway;

     case ipMRouteInIfIndex:
        long_return = gt->gt_route->rt_parent;
        return (u_char *) &long_return;

     case ipMRouteUpTime: {
        time_t currtime;
        time(&currtime);
        long_return = (currtime - gt->gt_ctime)*100;
        return (u_char *) &long_return;
     }

     case ipMRouteExpiryTime:
        long_return = 5*((gt->gt_timer+4)/5); /* round up to nearest 5 */
        long_return = (long_return + secs_remaining_offset()) * 100;
        return (u_char *) &long_return;

     case ipMRoutePkts:
        refresh_sg(&sg_req, gt, st);
        long_return = sg_req.pktcnt;
        return (u_char *) &long_return;

     case ipMRouteOctets:
        refresh_sg(&sg_req, gt, st);
        long_return = sg_req.bytecnt;
        return (u_char *) &long_return;

     case ipMRouteDifferentInIfIndexes:
        refresh_sg(&sg_req, gt, st);
        long_return = sg_req.wrong_if;
        return (u_char *) &long_return;

     case ipMRouteProtocol:
        long_return = 4;
        return (u_char *) &long_return;

   default:
      ERROR("");
   }
   return NULL;
}

/*
* Implements the IP Multicast Routing Next Hop Table portion of the Multicast
* MIB
*/
u_char *
o_ipMRouteNextHopTable(vp, name, length, exact, var_len, write_method)
   struct variable *vp;   /* IN - pointer to variable entry that points here */
   oid *name;      /* IN/OUT - input name requested, output name found */
   int *length;    /* IN/OUT - length of input and output oid's */
   int                 exact;      /* IN - TRUE if an exact match was requested. */
   int                 *var_len;   /* OUT - length of variable or 0 if function returned. */
   int                 (**write_method)(); /* OUT - pointer to function to set variable, otherwise 0 */
{
   u_long src, grp, mask, addr;
   vifi_t   vifi;
   struct gtable *gt;
   struct stable *st;
   oid        newname[MAX_NAME_LEN];
   int        len;

   /* Copy name OID to new OID */
   bcopy((char *)vp->name, (char *)newname, (int)vp->namelen * sizeof(oid));

   if (exact) {
           if (*length != vp->namelen + 17)
               return NULL;

     if (!get_address(name, *length, &grp, vp->namelen)
      || !get_address(name, *length, &src, vp->namelen+4)
      || !get_address(name, *length, &mask, vp->namelen+8)
      || !get_address(name, *length, &addr, vp->namelen+13)
      || grp!=addr
      || mask!=0xFFFFFFFF
      || (!(gt=find_grp(grp)))
      || (!(st=find_grp_src(gt,src))))
               return NULL;

     vifi = name[vp->namelen+12];
     if (!(VIFM_ISSET(vifi, gt->gt_route->rt_children)))
     return NULL;

      bcopy((char *)name, (char *)newname, ((int)*length) * sizeof(oid));
        } else {
      len = *length;
      if (compare(name, *length, vp->name, vp->namelen) < 0)
         len = vp->namelen;

           if (len < vp->namelen + 17) { /* get first entry */

        get_address(name, len, &grp, vp->namelen);
        get_address(name, len, &src, vp->namelen+4);
        get_address(name, len, &mask, vp->namelen+8);

        /* Find first child vif */
        vifi=0;
        if (!next_child(&gt, &st, grp, src, mask, &vifi))
           return NULL;

        put_address(newname, gt->gt_mcastgrp, vp->namelen);
        put_address(newname, st->st_origin,   vp->namelen+4);
        put_address(newname, 0xFFFFFFFF,      vp->namelen+8);
               newname[vp->namelen+12] = vifi;
        put_address(newname, gt->gt_mcastgrp, vp->namelen+13);

           } else {  /* get next entry given previous */
                  vifi = name[vp->namelen+12]+1;
        if (!get_address(name, *length, &grp,  vp->namelen)
         || !get_address(name, *length, &src,  vp->namelen+4)
         || !get_address(name, *length, &mask, vp->namelen+8)
         || !next_child(&gt, &st, grp, src, mask, &vifi))
           return NULL;

        put_address(newname, gt->gt_mcastgrp, vp->namelen);
        put_address(newname, st->st_origin,   vp->namelen+4);
        put_address(newname, 0xFFFFFFFF,      vp->namelen+8);
                  newname[vp->namelen+12] = vifi;
        put_address(newname, gt->gt_mcastgrp, vp->namelen+13);
           }
   }

   /* Save new OID */
   *length = vp->namelen + 17;
   bcopy((char *)newname, (char *)name, ((int)*length) * sizeof(oid));
   *write_method = 0;
   *var_len = sizeof(long);

   switch (vp->magic) {

     case ipMRouteNextHopState:
        long_return = (VIFM_ISSET(vifi, gt->gt_grpmems))? 2 : 1;
        return (u_char *) &long_return;

     /* Currently equal to ipMRouteUpTime */
     case ipMRouteNextHopUpTime: {
        time_t currtime;
        time(&currtime);
        long_return = (currtime - gt->gt_ctime)*100;
        return (u_char *) &long_return;
     }

     case ipMRouteNextHopExpiryTime:
        long_return = 5*((gt->gt_prsent_timer+4)/5); /* round up to nearest 5*/
        long_return = (long_return + secs_remaining_offset()) * 100;
        return (u_char *) &long_return;

     case ipMRouteNextHopClosestMemberHops:
        long_return = 0;
        return (u_char *) &long_return;

     case ipMRouteNextHopProtocol:
        long_return = 4;
        return (u_char *) &long_return;

   default:
      ERROR("");
   }
   return NULL;
}

/* sync_timer is called by timer() every TIMER_INTERVAL seconds.
* Its job is to record this time so that we can compute on demand
* the approx # seconds remaining until the next timer() call
*/
static time_t lasttimer;

void
sync_timer()
{
   time(&lasttimer);
}

int /* in range [-TIMER_INTERVAL..0] */
secs_remaining_offset()
{
  time_t tm;

  time(&tm);
  return lasttimer-tm;
}