/*
 * snmp.c
 *
 * Code written by David Thaler <thalerd@eecs.umich.edu>
 * Moved to a seperate file by Bill Fenner <fenner@parc.xerox.com>
 *
 * $Id: snmp.c,v 3.6 1995/06/25 19:51:21 fenner Exp $
 */

#include "defs.h"
#include "snmp.h"

#define NUMMIBS 2
#define SNMPD_RETRY_INTERVAL 300 /* periodic snmpd probe interval */

char *mibs[]={ "ipMRouteMIB", "dvmrpMIB" };

extern int o_ipMRouteTable();
extern int o_ipMRouteNextHopTable();
extern int o_dvmrpRouteTable();
extern int o_dvmrpRouteNextHopTable();

       int smux_fd = NOTOK;
       int rock_and_roll = 0;
       int dont_bother_anymore = 0;
       int quantum = 0;
static OID   subtree[NUMMIBS] = NULLOID;
static struct smuxEntry *se = NULL;
extern int smux_errno;
extern char smux_info[BUFSIZ];

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

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

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

   (*addr) = 0;

   if (oid -> oid_nelem < n+4)
      return 0;

   for (i=n; i<n+4; i++) {
      (*addr) <<= 8;
      if (i >= oid->oid_nelem)
          ok = 0;
      else
         (*addr) |= oid->oid_elements[i];
   }

   return ok;
}

/*
 *  Attempt to start up SMUX protocol
 */
void
try_smux_init()
{
    if (smux_fd != NOTOK || dont_bother_anymore) 
       return;
    if ((smux_fd = smux_init(debug)) == NOTOK) {
       log(LOG_WARNING, 0,"smux_init: %s [%s]", smux_error(smux_errno), 
        smux_info);
    } else 
       rock_and_roll = 0;
}

/* 
 * Implements scalar objects from both MIBs 
 */
static int 
o_scalar(oi, v, offset)
   OI oi;
   register struct type_SNMP_VarBind *v;
   int offset;
{
    int     ifvar;
    register OID    oid = oi -> oi_name;
    register OT     ot = oi -> oi_type;

    ifvar = (int) ot -> ot_info;
    switch (offset) {
        case type_SNMP_SMUX__PDUs_get__request:
            if (oid -> oid_nelem !=
                        ot -> ot_name -> oid_nelem + 1
                    || oid -> oid_elements[oid -> oid_nelem - 1]
                            != 0)
                return int_SNMP_error__status_noSuchName;
            break;

        case type_SNMP_SMUX__PDUs_get__next__request:
            if (oid -> oid_nelem
                    == ot -> ot_name -> oid_nelem) {
                OID     new;

                if ((new = oid_extend (oid, 1)) == NULLOID)
                    return int_SNMP_error__status_genErr;
                new -> oid_elements[new -> oid_nelem - 1] = 0;

                if (v -> name)
                    free_SNMP_ObjectName (v -> name);
                v -> name = new;
            }
            else
                return NOTOK;
            break;

        default:
            return int_SNMP_error__status_genErr;
    }

    switch (ifvar) {
        case ipMRouteEnable:
            return o_integer (oi, v, 1);

        case dvmrpVersion: {
            static char buff[15];

            sprintf(buff, "mrouted%d.%d", PROTOCOL_VERSION, MROUTED_VERSION);
            return o_string (oi, v, buff, strlen (buff));
        }

        case dvmrpGenerationId:
            return o_integer (oi, v, dvmrp_genid);

        default:
            return int_SNMP_error__status_noSuchName;
    }
}

/* 
 * Find if a specific scoped boundary exists on a Vif
 */
struct vif_acl *
find_boundary(vifi, addr, mask)
   int vifi;
   int addr;
   int 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 next scoped boundary in order after a given spec
 */
struct vif_acl *
next_boundary(vifi, addr, mask)
   int *vifi;
   int  addr;
   int  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
 */
static int  
o_dvmrpBoundaryTable (oi, v, offset)
OI	oi;
register struct type_SNMP_VarBind *v;
{
    int	    ifvar, vifi,
	    addr, mask;
    register OID    oid = oi -> oi_name;
    register OT	   ot = oi -> oi_type;
    struct vif_acl *bound;

    ifvar = (int) ot -> ot_info;
    switch (offset) {
	case type_SNMP_SMUX__PDUs_get__request:
	    if (oid->oid_nelem != ot->ot_name->oid_nelem + 9)
		return int_SNMP_error__status_noSuchName;

      if ((vifi = oid -> oid_elements[ot-> ot_name->oid_nelem]) >= numvifs)
      return int_SNMP_error__status_noSuchName;

      if (!get_address(oid, &addr, ot->ot_name->oid_nelem+1)
       || !get_address(oid, &mask, ot->ot_name->oid_nelem+5))
		return int_SNMP_error__status_noSuchName;

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

	case type_SNMP_SMUX__PDUs_get__next__request:
	    if (oid->oid_nelem < ot->ot_name->oid_nelem + 9) {
		OID	new;

      if (oid->oid_nelem == ot->ot_name->oid_nelem) {
         vifi = addr = mask = 0;
      } else {
         vifi = oid->oid_elements[ot->ot_name->oid_nelem];
         get_address(oid, &addr, ot->ot_name->oid_nelem+1);
         get_address(oid, &mask, ot->ot_name->oid_nelem+5);
      }

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

		new = oid_extend (oid, ot->ot_name->oid_nelem+9-oid->oid_nelem);
		if (new == NULLOID)
		    return NOTOK;
		new -> oid_elements[ot->ot_name->oid_nelem] = vifi;
      put_address(new, bound->acl_addr, ot->ot_name->oid_nelem+1);
      put_address(new, bound->acl_mask, ot->ot_name->oid_nelem+5);

		if (v -> name)
		    free_SNMP_ObjectName (v -> name);
		v -> name = new;
	    } else {  /* get next entry given previous */
		int	i = ot -> ot_name -> oid_nelem;

		   vifi = oid->oid_elements[i];
         get_address(oid, &addr, ot->ot_name->oid_nelem+1);
         get_address(oid, &mask, ot->ot_name->oid_nelem+5);
         if (!(bound = next_boundary(&vifi,addr,mask+1)))
            return NOTOK;

         put_address(oid, bound->acl_addr, ot->ot_name->oid_nelem+1);
         put_address(oid, bound->acl_mask, ot->ot_name->oid_nelem+5);
		   oid->oid_elements[i] = vifi;
		   oid->oid_nelem = i + 9;
	    }
	    break;

	default:
	    return int_SNMP_error__status_genErr;
    }

    switch (ifvar) {

   case dvmrpBoundaryVifIndex:
       return o_integer (oi, v, vifi);

	default:
	    return int_SNMP_error__status_noSuchName;
    }
}

/* 
 * Given a vif index and address, return the next greater neighbor entry 
 */
struct listaddr *
next_neighbor(vifi, addr)
   int *vifi;
   int  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)
   int vifi;
   int 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;
}

/*
 * Implements the Neighbor Table portion of the DVMRP MIB
 */
static int  
o_dvmrpNeighborTable (oi, v, offset)
OI	oi;
register struct type_SNMP_VarBind *v;
{
    int	    ifvar, vifi,
	    addr;
    register OID    oid = oi -> oi_name;
    register OT	   ot = oi -> oi_type;
    struct listaddr *neighbor;

    ifvar = (int) ot -> ot_info;
    switch (offset) {
	case type_SNMP_SMUX__PDUs_get__request:
	    if (oid->oid_nelem != ot->ot_name->oid_nelem + 5)
		return int_SNMP_error__status_noSuchName;

      if ((vifi = oid -> oid_elements[ot-> ot_name->oid_nelem]) >= numvifs)
      return int_SNMP_error__status_noSuchName;

      if (!get_address(oid, &addr, ot->ot_name->oid_nelem+1))
		return int_SNMP_error__status_noSuchName;

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

	case type_SNMP_SMUX__PDUs_get__next__request:
	    if (oid->oid_nelem < ot->ot_name->oid_nelem + 5) { 
		OID	new;

      if (oid->oid_nelem == ot->ot_name->oid_nelem) {
         vifi = addr = 0;
      } else {
         vifi = oid->oid_elements[ot->ot_name->oid_nelem];
         get_address(oid, &addr, ot->ot_name->oid_nelem+1);
      }

      neighbor = next_neighbor(&vifi,addr); /* Get first entry */
      if (!neighbor)
         return NOTOK;

		new = oid_extend (oid, ot->ot_name->oid_nelem+5-oid->oid_nelem);
		if (new == NULLOID)
		    return NOTOK;
		new -> oid_elements[ot->ot_name->oid_nelem] = vifi;
      put_address(new, neighbor->al_addr, ot->ot_name->oid_nelem+1);

		if (v -> name)
		    free_SNMP_ObjectName (v -> name);
		v -> name = new;

	    } else {  /* get next entry given previous */
		int	i = ot -> ot_name -> oid_nelem;

		   vifi = oid->oid_elements[i];
         get_address(oid, &addr, ot->ot_name->oid_nelem+1);
         if (!(neighbor = next_neighbor(&vifi,addr+1)))
            return NOTOK;

         put_address(oid, neighbor->al_addr, ot->ot_name->oid_nelem+1);
		   oid->oid_elements[i] = vifi;
		   oid->oid_nelem = i + 5;
	    }
	    break;

	default:
	    return int_SNMP_error__status_genErr;
    }

    switch (ifvar) {

   case dvmrpNeighborUpTime: {
       time_t currtime;
       time(&currtime);
       return o_integer (oi, v, (currtime - neighbor->al_ctime)*100);
   }

   case dvmrpNeighborExpiryTime:
       return o_integer (oi, v, (NEIGHBOR_EXPIRE_TIME-neighbor->al_timer) * 100);

   case dvmrpNeighborVersion: {
       static char buff[15];

       sprintf(buff, "%d.%d", neighbor->al_pv, neighbor->al_mv);
       return o_string (oi, v, buff, strlen (buff));
   }

   case dvmrpNeighborGenerationId: 
       return o_integer (oi, v, neighbor->al_genid);

	default:
	    return int_SNMP_error__status_noSuchName;
    }
}

/*
 * 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(udp_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
 */
static int  
o_ipMRouteInterfaceTable (oi, v, offset)
OI	oi;
register struct type_SNMP_VarBind *v;
int	offset;
{
    int	    ifnum,
	    ifvar;
    register OID    oid = oi -> oi_name;
    register OT	   ot = oi -> oi_type;
static struct sioc_vif_req v_req;

    ifvar = (int) ot -> ot_info;
    switch (offset) {
	case type_SNMP_SMUX__PDUs_get__request:
	    if (oid -> oid_nelem != ot -> ot_name -> oid_nelem + 1)
		return int_SNMP_error__status_noSuchName;
	    if ((ifnum = oid -> oid_elements[oid -> oid_nelem - 1]) >= numvifs)
		return int_SNMP_error__status_noSuchName;
	    break;

	case type_SNMP_SMUX__PDUs_get__next__request:
	    if (oid -> oid_nelem == ot -> ot_name -> oid_nelem) {
		OID	new;

		ifnum = 0;

		if ((new = oid_extend (oid, 1)) == NULLOID)
		    return NOTOK;
		new -> oid_elements[new -> oid_nelem - 1] = ifnum;

		if (v -> name)
		    free_SNMP_ObjectName (v -> name);
		v -> name = new;

	    } else {
		int	i = ot -> ot_name -> oid_nelem;

		if ((ifnum = oid -> oid_elements[i] + 1) >= numvifs)
		    return NOTOK;

		oid -> oid_elements[i] = ifnum;
		oid -> oid_nelem = i + 1;
	    }
	    break;

	default:
	    return int_SNMP_error__status_genErr;
    }

    switch (ifvar) {
	case ipMRouteInterfaceTtl:
	    return o_integer (oi, v, uvifs[ifnum].uv_threshold);

	case dvmrpVInterfaceType:
      if (uvifs[ifnum].uv_flags & VIFF_SRCRT)
         return o_integer (oi, v, 2); 
      else if (uvifs[ifnum].uv_flags & VIFF_TUNNEL)
         return o_integer (oi, v, 1); 
      else if (uvifs[ifnum].uv_flags & VIFF_QUERIER)
         return o_integer (oi, v, 3); 
      else                               /* SUBNET */
         return o_integer (oi, v, 4); 

	case dvmrpVInterfaceState: 
      if (uvifs[ifnum].uv_flags & VIFF_DISABLED)
         return o_integer (oi, v, 3);
      else if (uvifs[ifnum].uv_flags & VIFF_DOWN)
         return o_integer (oi, v, 2);
      else /* UP */
         return o_integer (oi, v, 1); 

   case dvmrpVInterfaceLocalAddress: {
      struct sockaddr_in tmp;
      tmp.sin_addr.s_addr = uvifs[ifnum].uv_lcl_addr;
      return o_ipaddr (oi, v, &tmp);
   }

   case dvmrpVInterfaceRemoteAddress: {
      struct sockaddr_in tmp;
      tmp.sin_addr.s_addr = (uvifs[ifnum].uv_flags & VIFF_TUNNEL) ?
         uvifs[ifnum].uv_rmt_addr :
         uvifs[ifnum].uv_subnet;
      return o_ipaddr (oi, v, &tmp);
   }

   case dvmrpVInterfaceRemoteSubnetMask: {
      struct sockaddr_in tmp;
      tmp.sin_addr.s_addr = uvifs[ifnum].uv_subnetmask;
      return o_ipaddr (oi, v, &tmp);
   }

	case dvmrpVInterfaceMetric:
	    return o_integer (oi, v, uvifs[ifnum].uv_metric);

	case dvmrpVInterfaceRateLimit:
	    return o_integer (oi, v, uvifs[ifnum].uv_rate_limit);

	case dvmrpVInterfaceInPkts:
       refresh_vif(&v_req, ifnum);
	    return o_integer(oi, v, v_req.icount);

	case dvmrpVInterfaceOutPkts:
       refresh_vif(&v_req, ifnum);
	    return o_integer(oi, v, v_req.ocount);

	case dvmrpVInterfaceInOctets:
       refresh_vif(&v_req, ifnum);
	    return o_integer(oi, v, v_req.ibytes);

	case dvmrpVInterfaceOutOctets:
       refresh_vif(&v_req, ifnum);
	    return o_integer(oi, v, v_req.obytes);

	default:
	    return int_SNMP_error__status_noSuchName;
    }
}

struct mib_variable {
   char     *name;        /* MIB variable name */
   int     (*function)(); /* Function to call */
   int       info;        /* Which variable */
} mib_vars[] = {
 "ipMRouteEnable",               o_scalar, ipMRouteEnable,
 "ipMRouteUpstreamNeighbor",     o_ipMRouteTable,  ipMRouteUpstreamNeighbor,
 "ipMRouteInIfIndex",            o_ipMRouteTable,  ipMRouteInIfIndex,
 "ipMRouteUpTime",               o_ipMRouteTable,  ipMRouteUpTime, 
 "ipMRouteExpiryTime",           o_ipMRouteTable,  ipMRouteExpiryTime,
 "ipMRoutePkts",                 o_ipMRouteTable,  ipMRoutePkts, 
 "ipMRouteDifferentInIfIndexes", o_ipMRouteTable,  ipMRouteDifferentInIfIndexes,
 "ipMRouteOctets",               o_ipMRouteTable,  ipMRouteOctets,
 "ipMRouteProtocol",             o_ipMRouteTable,  ipMRouteProtocol,
 "ipMRouteNextHopState",      o_ipMRouteNextHopTable, ipMRouteNextHopState,
 "ipMRouteNextHopUpTime",     o_ipMRouteNextHopTable, ipMRouteNextHopUpTime,
 "ipMRouteNextHopExpiryTime", o_ipMRouteNextHopTable, ipMRouteNextHopExpiryTime,
 "ipMRouteNextHopClosestMemberHops", o_ipMRouteNextHopTable, ipMRouteNextHopClosestMemberHops,
 "ipMRouteNextHopProtocol",   o_ipMRouteNextHopTable, ipMRouteNextHopProtocol,
 "ipMRouteInterfaceTtl",  o_ipMRouteInterfaceTable, ipMRouteInterfaceTtl,
 "dvmrpVersion",               o_scalar, dvmrpVersion,
 "dvmrpGenerationId",          o_scalar, dvmrpGenerationId,
 "dvmrpVInterfaceType",     o_ipMRouteInterfaceTable, dvmrpVInterfaceType,
 "dvmrpVInterfaceState",    o_ipMRouteInterfaceTable, dvmrpVInterfaceState,
 "dvmrpVInterfaceLocalAddress", o_ipMRouteInterfaceTable, dvmrpVInterfaceLocalAddress,
 "dvmrpVInterfaceRemoteAddress", o_ipMRouteInterfaceTable, dvmrpVInterfaceRemoteAddress,
 "dvmrpVInterfaceRemoteSubnetMask", o_ipMRouteInterfaceTable, dvmrpVInterfaceRemoteSubnetMask,
 "dvmrpVInterfaceMetric",    o_ipMRouteInterfaceTable, dvmrpVInterfaceMetric,
 "dvmrpVInterfaceRateLimit", o_ipMRouteInterfaceTable, dvmrpVInterfaceRateLimit,
 "dvmrpVInterfaceInPkts",    o_ipMRouteInterfaceTable, dvmrpVInterfaceInPkts,
 "dvmrpVInterfaceOutPkts",   o_ipMRouteInterfaceTable, dvmrpVInterfaceOutPkts,
 "dvmrpVInterfaceInOctets",  o_ipMRouteInterfaceTable, dvmrpVInterfaceInOctets,
 "dvmrpVInterfaceOutOctets", o_ipMRouteInterfaceTable, dvmrpVInterfaceOutOctets,
 "dvmrpNeighborUpTime",      o_dvmrpNeighborTable, dvmrpNeighborUpTime,
 "dvmrpNeighborExpiryTime",  o_dvmrpNeighborTable, dvmrpNeighborExpiryTime,
 "dvmrpNeighborVersion",     o_dvmrpNeighborTable, dvmrpNeighborVersion,
 "dvmrpNeighborGenerationId",o_dvmrpNeighborTable, dvmrpNeighborGenerationId,
 "dvmrpRouteUpstreamNeighbor", o_dvmrpRouteTable, dvmrpRouteUpstreamNeighbor,
 "dvmrpRouteInVifIndex",       o_dvmrpRouteTable, dvmrpRouteInVifIndex,
 "dvmrpRouteMetric",           o_dvmrpRouteTable, dvmrpRouteMetric,
 "dvmrpRouteExpiryTime",       o_dvmrpRouteTable, dvmrpRouteExpiryTime,
 "dvmrpRouteNextHopType",    o_dvmrpRouteNextHopTable, dvmrpRouteNextHopType,
 "dvmrpBoundaryVifIndex",    o_dvmrpBoundaryTable, dvmrpBoundaryVifIndex,
 0, 0, 0
};

/*
 * Register variables as part of the MIBs
 */
void
init_mib()
{
   register OT ot;
   int i;

   for (i=0; mib_vars[i].name; i++)
      if (ot=text2obj(mib_vars[i].name)) {
         ot->ot_getfnx = mib_vars[i].function;
         ot->ot_info = (caddr_t)mib_vars[i].info;
      }
}

/*
 * Initialize the SNMP part of mrouted
 */
void
snmp_init()
{
    OT ot;
    int i;

    if (readobjects("mrouted.defs") == NOTOK)
       log(LOG_ERR, 0, "readobjects: %s", PY_pepy);
    for (i=0; i < NUMMIBS; i++) {
       if ((ot = text2obj(mibs[i])) == NULL)
          log(LOG_ERR, 0, "object \"%s\" not in \"%s\"",
		  mibs[i], "mrouted.defs");
       subtree[i] = ot -> ot_name;
    }
    init_mib();
    try_smux_init();
}

/*
 * Process an SNMP "get" or "get-next" request
 */
static  
get_smux (pdu, offset)
   register struct type_SNMP_GetRequest__PDU *pdu;
   int     offset;
{
    int     idx,
            status;
    object_instance ois;
    register struct type_SNMP_VarBindList *vp;
    IFP method;

    quantum = pdu -> request__id;
    idx = 0;
    for (vp = pdu -> variable__bindings; vp; vp = vp -> next) {
        register OI     oi;
        register OT     ot;
        register struct type_SNMP_VarBind *v = vp -> VarBind;

        idx++;

        if (offset == type_SNMP_SMUX__PDUs_get__next__request) {
            if ((oi = name2inst (v -> name)) == NULLOI
                    && (oi = next2inst (v -> name)) == NULLOI)
                goto no_name;

            if ((ot = oi -> oi_type) -> ot_getfnx == NULLIFP)
                goto get_next;
        }
        else
            if ((oi = name2inst (v -> name)) == NULLOI
                    || (ot = oi -> oi_type) -> ot_getfnx
                            == NULLIFP) {
no_name: ;
                pdu -> error__status =
                        int_SNMP_error__status_noSuchName;
                goto out;
            }

try_again: ;
   switch (offset) {
       case type_SNMP_SMUX__PDUs_get__request:
      if (!(method = ot -> ot_getfnx))
          goto no_name;
      break;

       case type_SNMP_SMUX__PDUs_get__next__request:
    if (!(method = ot -> ot_getfnx))
          goto get_next;
      break;

       case type_SNMP_SMUX__PDUs_set__request:
      if (!(method = ot -> ot_setfnx))
          goto no_name;
      break;

       default:
      goto no_name;
   }

        switch (status = (*ot -> ot_getfnx) (oi, v, offset)) {
            case NOTOK:     /* get-next wants a bump */
get_next: ;
                oi = &ois;
                for (;;) {
                    if ((ot = ot -> ot_next) == NULLOT) {
                        pdu -> error__status =
                              int_SNMP_error__status_noSuchName;
                        goto out;
                    }
                    oi -> oi_name =
                                (oi -> oi_type = ot) -> ot_name;
                    if (ot -> ot_getfnx)
                        goto try_again;
                }

            case int_SNMP_error__status_noError:
                break;

            default:
                pdu -> error__status = status;
                goto out;
        }
    }
    idx = 0;

out: ;
    pdu -> error__index = idx;

    if (smux_response (pdu) == NOTOK) {
        log(LOG_WARNING,0,"smux_response: %s [%s]",
               smux_error (smux_errno), smux_info);
        smux_fd = NOTOK;
    }
}

/*
 * Handle SNMP "set" request by replying that it is illegal
 */
static  
set_smux(event)
   struct type_SNMP_SMUX__PDUs *event;
{
    switch (event -> offset) {
        case type_SNMP_SMUX__PDUs_set__request:
            {
                register struct type_SNMP_GetResponse__PDU *pdu =
                                    event -> un.get__response;

                pdu -> error__status = int_SNMP_error__status_noSuchName;
                pdu -> error__index = pdu -> variable__bindings ? 1 : 0;

                if (smux_response (pdu) == NOTOK) {
                    log(LOG_WARNING, 0,
                            "smux_response: %s [%s]",
                            smux_error (smux_errno),
                            smux_info);
                    smux_fd = NOTOK;
                }
            }
            break;

        case type_SNMP_SMUX__PDUs_commitOrRollback:
            {
                struct type_SNMP_SOutPDU *cor =
                                event -> un.commitOrRollback;

                if (cor -> parm == int_SNMP_SOutPDU_commit) {
                                    /* "should not happen" */
                    (void) smux_close (protocolError);
                    smux_fd = NOTOK;
                }
            }
            break;
    }
}

/* 
 *  Handle an incoming SNMP message
 */
void
doit_smux()
{
   struct type_SNMP_SMUX__PDUs *event;
 
   if (smux_wait(&event, NOTOK)==NOTOK) {
      if (smux_errno==inProgress)
         return;
      log(LOG_WARNING, 0, "smux_wait: %s [%s]", smux_error(smux_errno), 
       smux_info);
      smux_fd = NOTOK;
      return;
   }

   switch (event -> offset) {
    case type_SNMP_SMUX__PDUs_registerResponse:
        {
            struct type_SNMP_RRspPDU *rsp =
                        event -> un.registerResponse;

            if (rsp -> parm == int_SNMP_RRspPDU_failure) {
                log(LOG_WARNING,0,"SMUX registration of subtree failed");
                dont_bother_anymore = 1;
                (void) smux_close (goingDown);
                break;
            }
        }
        if (smux_trap(NULLOID, int_SNMP_generic__trap_coldStart, 0,
                       (struct type_SNMP_VarBindList *)0) == NOTOK) {
            log(LOG_WARNING,0,"smux_trap: %s [%s]", smux_error (smux_errno), 
             smux_info);
            break;
        }
        return;

    case type_SNMP_SMUX__PDUs_get__request:
    case type_SNMP_SMUX__PDUs_get__next__request:
        get_smux (event -> un.get__request, event -> offset);
        return;

    case type_SNMP_SMUX__PDUs_close:
        log(LOG_WARNING, 0, "SMUX close: %s", 
         smux_error (event -> un.close -> parm));
        break;

    case type_SNMP_SMUX__PDUs_set__request:
    case type_SNMP_SMUX__PDUs_commitOrRollback:
        set_smux (event);
        return;

    default:
        log(LOG_WARNING,0,"bad SMUX operation: %d", event -> offset);
        (void) smux_close (protocolError);
        break;
   }
   smux_fd = NOTOK;
}

/* 
 * Inform snmpd that we are here and handling our MIBs
 */
void
start_smux()
{
   int i;

   for (i=0; i<NUMMIBS; i++) {
      if ((se = getsmuxEntrybyname (mibs[i])) == NULL) {
         log(LOG_WARNING,0,"no SMUX entry for \"%s\"", mibs[i]);
         return;
      }
 
      /* Only open a new connection the first time through */
      if (!i) {
         if (smux_simple_open(&se->se_identity, mibs[i], 
          se->se_password, strlen(se->se_password))==NOTOK) {
            if (smux_errno == inProgress)
               return;

            log(LOG_WARNING, 0,"smux_simple_open: %s [%s]", 
             smux_error(smux_errno), smux_info);
            smux_fd = NOTOK;
            return;
         }
         log(LOG_NOTICE,0, "SMUX open: %s \"%s\"",
          oid2ode (&se->se_identity), se->se_name);
         rock_and_roll = 1;
      }

      if (smux_register(subtree[i], -1, readWrite)==NOTOK) {
         log(LOG_WARNING, 0,"smux_register: %s [%s]", smux_error(smux_errno), 
          smux_info);
         smux_fd = NOTOK;
         return;
      }
   }
   log(LOG_NOTICE, 0, "SMUX registered");
}

/*
 * Implements the DVMRP Route Table portion of the DVMRP MIB 
 */
int
o_dvmrpRouteTable (oi, v, offset)
OI oi;
register struct type_SNMP_VarBind *v;
int	offset;
{
    u_long   src, mask;
    int	    ifvar;
    register OID    oid = oi -> oi_name;
    register OT	    ot = oi -> oi_type;
    struct rtentry *rt = NULL;

    ifvar = (int) ot -> ot_info;
    switch (offset) {
	case type_SNMP_SMUX__PDUs_get__request:
      if (!get_address(oid, &src, ot->ot_name->oid_nelem)
       || !get_address(oid, &mask, ot->ot_name->oid_nelem+4)
       || !(rt = snmp_find_route(src,mask)))
         return int_SNMP_error__status_noSuchName;
      break;

	case type_SNMP_SMUX__PDUs_get__next__request:

       /* Check if we're requesting the first row */
      if (oid->oid_nelem < ot->ot_name->oid_nelem+8) {
         OID	new;

         /* Get partial specification (if any) */
         get_address(oid, &src, ot->ot_name->oid_nelem);
         get_address(oid, &mask, ot->ot_name->oid_nelem+4);

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

         /* Extend by 8 more ints to hold index columns */
         new = oid_extend (oid, ot->ot_name->oid_nelem+8-oid->oid_nelem);
         if (new == NULLOID)
            return NOTOK;

         put_address(new, rt->rt_origin,     ot->ot_name->oid_nelem);
         put_address(new, rt->rt_originmask, ot->ot_name->oid_nelem+4); 

         if (v -> name)
            free_SNMP_ObjectName (v -> name);
         v -> name = new;

      /* Else we start from a previous row */
      } else {
         int	i = ot -> ot_name -> oid_nelem;

         /* Get the lowest entry in the table > the given grp/src/mask */
         get_address(oid, &src, ot->ot_name->oid_nelem);
         get_address(oid, &mask, ot->ot_name->oid_nelem+4);
         if (!next_route(&rt, src,mask))
            return NOTOK; 

         put_address(oid, rt->rt_origin, ot->ot_name->oid_nelem);
         put_address(oid, rt->rt_originmask, ot->ot_name->oid_nelem+4);
      }
      break;

	default:
	   return int_SNMP_error__status_genErr;
   }

   switch (ifvar) {
      case dvmrpRouteUpstreamNeighbor: {
         struct sockaddr_in tmp;
         tmp.sin_addr.s_addr = rt->rt_gateway;
         return o_ipaddr (oi, v, &tmp);
      }

      case dvmrpRouteInVifIndex:
         return o_integer (oi, v, rt->rt_parent);

      case dvmrpRouteMetric:
         return o_integer (oi, v, rt->rt_metric);

      case dvmrpRouteExpiryTime:
         return o_integer (oi, v, rt->rt_timer*100);

      default:
         return int_SNMP_error__status_noSuchName;
   }
}

/* 
 * Implements the DVMRP Routing Next Hop Table portion of the DVMRP MIB 
 */
int
o_dvmrpRouteNextHopTable (oi, v, offset)
OI oi;
register struct type_SNMP_VarBind *v;
int   offset;
{
    u_long   src, mask;
    vifi_t   vifi;
    int	    ifvar;
    register OID    oid = oi -> oi_name;
    register OT	    ot = oi -> oi_type;
    struct rtentry *rt = NULL;

    ifvar = (int) ot -> ot_info;
    switch (offset) {
	case type_SNMP_SMUX__PDUs_get__request:
      if (oid->oid_nelem != ot->ot_name->oid_nelem+9)
         return int_SNMP_error__status_noSuchName;

      if (!get_address(oid, &src, ot->ot_name->oid_nelem)
       || !get_address(oid, &mask, ot->ot_name->oid_nelem+4)
       || (!(rt=snmp_find_route(src,mask))))
         return int_SNMP_error__status_noSuchName;

      vifi = oid->oid_elements[ot->ot_name->oid_nelem+8];
      if (!(VIFM_ISSET(vifi, rt->rt_children)))
         return int_SNMP_error__status_noSuchName;
      break;

	case type_SNMP_SMUX__PDUs_get__next__request:

      /* Check if we're requesting the first row */
      if (oid->oid_nelem < ot->ot_name->oid_nelem+9) {
         OID	new;

         get_address(oid, &src, ot->ot_name->oid_nelem);
         get_address(oid, &mask, ot->ot_name->oid_nelem+4);

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

         /* Extend by 9 more ints to hold index columns */
         new = oid_extend (oid, ot->ot_name->oid_nelem+9-oid->oid_nelem);
         if (new == NULLOID)
            return NOTOK;

         put_address(new, rt->rt_origin, ot->ot_name->oid_nelem);
         put_address(new, rt->rt_originmask, ot->ot_name->oid_nelem+4);
         new->oid_elements[ot->ot_name->oid_nelem+8] = vifi;

         if (v -> name)
            free_SNMP_ObjectName (v -> name);
         v -> name = new;

      /* Else we start from a previous row */
      } else {
         int	i = ot -> ot_name -> oid_nelem;

         /* Get the lowest entry in the table > the given grp/src/mask */
         vifi = oid->oid_elements[oid->oid_nelem-1] + 1;
         if (!get_address(oid, &src, ot->ot_name->oid_nelem)
          || !get_address(oid, &mask, ot->ot_name->oid_nelem+4)
          || !next_route_child(&rt, src, mask, &vifi))
            return NOTOK;

         put_address(oid, rt->rt_origin, ot->ot_name->oid_nelem);
         put_address(oid, rt->rt_originmask, ot->ot_name->oid_nelem+4);
         oid->oid_elements[ot->ot_name->oid_nelem+8] = vifi;
      }
      break;

	default:
	   return int_SNMP_error__status_genErr;
   }

   switch (ifvar) {

      case dvmrpRouteNextHopType:
         return o_integer (oi, v, (VIFM_ISSET(vifi, rt->rt_leaves))? 1 : 2);

      default:
         return int_SNMP_error__status_noSuchName;
   }
}

/* 
 * Implements the IP Multicast Route Table portion of the Multicast MIB 
 */
int  
o_ipMRouteTable (oi, v, offset)
OI	oi;
register struct type_SNMP_VarBind *v;
int	offset;
{
    u_long src, grp, mask;
    int	    ifvar;
    register OID    oid = oi -> oi_name;
    register OT	    ot = oi -> oi_type;
    struct gtable *gt = NULL;
    struct stable *st = NULL;
static struct sioc_sg_req sg_req;

    ifvar = (int) ot -> ot_info;
    switch (offset) {
	case type_SNMP_SMUX__PDUs_get__request:
      if (!get_address(oid, &grp, ot->ot_name->oid_nelem)
       || !get_address(oid, &src, ot->ot_name->oid_nelem+4)
       || !get_address(oid, &mask, ot->ot_name->oid_nelem+8)
       || (mask != 0xFFFFFFFF) /* we keep sources now, not subnets */
       || !(gt = find_grp(grp))
       || !(st = find_grp_src(gt,src)))
         return int_SNMP_error__status_noSuchName;
      break;

	case type_SNMP_SMUX__PDUs_get__next__request:

       /* Check if we're requesting the first row */
      if (oid->oid_nelem < ot->ot_name->oid_nelem+12) {
         OID	new;

         /* Get partial specification (if any) */
         get_address(oid, &grp, ot->ot_name->oid_nelem);
         get_address(oid, &src, ot->ot_name->oid_nelem+4);
         get_address(oid, &mask, ot->ot_name->oid_nelem+8);

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

         /* Extend by 12 more ints to hold index columns */
         new = oid_extend (oid, ot->ot_name->oid_nelem+12-oid->oid_nelem);
         if (new == NULLOID)
            return NOTOK;

         put_address(new, gt->gt_mcastgrp, ot->ot_name->oid_nelem);
         put_address(new, st->st_origin, ot->ot_name->oid_nelem+4);
         put_address(new, 0xFFFFFFFF, ot->ot_name->oid_nelem+8); 

         if (v -> name)
            free_SNMP_ObjectName (v -> name);
         v -> name = new;

      /* Else we start from a previous row */
      } else {
         int	i = ot -> ot_name -> oid_nelem;

         /* Get the lowest entry in the table > the given grp/src/mask */
         get_address(oid, &grp, ot->ot_name->oid_nelem);
         get_address(oid, &src, ot->ot_name->oid_nelem+4);
         get_address(oid, &mask, ot->ot_name->oid_nelem+8);
         if (!next_grp_src_mask(&gt, &st, grp,src,mask))
            return NOTOK; 

         put_address(oid, gt->gt_mcastgrp, ot->ot_name->oid_nelem);
         put_address(oid, st->st_origin, ot->ot_name->oid_nelem+4);
         put_address(oid, 0xFFFFFFFF, ot->ot_name->oid_nelem+8);
      }
      break;

	default:
	   return int_SNMP_error__status_genErr;
   }

   switch (ifvar) {
      case ipMRouteUpstreamNeighbor: {
         struct sockaddr_in tmp;
         tmp.sin_addr.s_addr = gt->gt_route->rt_gateway;
         return o_ipaddr (oi, v, &tmp);
      }

      case ipMRouteInIfIndex:
         return o_integer (oi, v, gt->gt_route->rt_parent);

      case ipMRouteUpTime: {
         time_t currtime;
         time(&currtime);
         return o_integer (oi, v, (currtime - gt->gt_ctime)*100);
      }

      case ipMRouteExpiryTime:
         return o_integer (oi, v, gt->gt_timer*100);

      case ipMRoutePkts:
         refresh_sg(&sg_req, gt, st);
         return o_integer (oi, v, sg_req.pktcnt);
    
      case ipMRouteOctets:
         refresh_sg(&sg_req, gt, st);
         return o_integer (oi, v, sg_req.bytecnt);

      case ipMRouteDifferentInIfIndexes:
         refresh_sg(&sg_req, gt, st);
         return o_integer (oi, v, sg_req.wrong_if);

      case ipMRouteProtocol:
         return o_integer (oi, v, 4);

      default:
         return int_SNMP_error__status_noSuchName;
   }
}

/* 
 * Implements the IP Multicast Routing Next Hop Table portion of the Multicast
 * MIB 
 */
int  
o_ipMRouteNextHopTable (oi, v, offset)
OI	oi;
register struct type_SNMP_VarBind *v;
int	offset;
{
    u_long src, grp, mask, addr;
    vifi_t   vifi;
    int	    ifvar;
    register OID    oid = oi -> oi_name;
    register OT	    ot = oi -> oi_type;
    struct gtable *gt;
    struct stable *st;

    ifvar = (int) ot -> ot_info;
    switch (offset) {
	case type_SNMP_SMUX__PDUs_get__request:
      if (oid->oid_nelem != ot->ot_name->oid_nelem+17)
         return int_SNMP_error__status_noSuchName;

      if (!get_address(oid, &grp, ot->ot_name->oid_nelem)
       || !get_address(oid, &src, ot->ot_name->oid_nelem+4)
       || !get_address(oid, &mask, ot->ot_name->oid_nelem+8)
       || !get_address(oid, &addr, ot->ot_name->oid_nelem+13)
       || grp!=addr
       || mask!=0xFFFFFFFF
       || (!(gt=find_grp(grp)))
       || (!(st=find_grp_src(gt,src))))
         return int_SNMP_error__status_noSuchName;

      vifi = oid->oid_elements[ot->ot_name->oid_nelem+12];
      if (!(VIFM_ISSET(vifi, gt->gt_route->rt_children)))
         return int_SNMP_error__status_noSuchName;
      break;

	case type_SNMP_SMUX__PDUs_get__next__request:

      /* Check if we're requesting the first row */
      if (oid->oid_nelem < ot->ot_name->oid_nelem+17) {
         OID	new;

         get_address(oid, &grp, ot->ot_name->oid_nelem);
         get_address(oid, &src, ot->ot_name->oid_nelem+4);
         get_address(oid, &mask, ot->ot_name->oid_nelem+8);

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

         /* Extend by 17 more ints to hold index columns */
         new = oid_extend (oid, ot->ot_name->oid_nelem+17-oid->oid_nelem);
         if (new == NULLOID)
            return NOTOK;

         put_address(new, gt->gt_mcastgrp, ot->ot_name->oid_nelem);
         put_address(new, st->st_origin, ot->ot_name->oid_nelem+4);
         put_address(new, 0xFFFFFFFF, ot->ot_name->oid_nelem+8);
         new->oid_elements[ot->ot_name->oid_nelem+12] = vifi;
         put_address(new, gt->gt_mcastgrp, ot->ot_name->oid_nelem+13);

         if (v -> name)
            free_SNMP_ObjectName (v -> name);
         v -> name = new;

      /* Else we start from a previous row */
      } else {
         int	i = ot -> ot_name -> oid_nelem;

         /* Get the lowest entry in the table > the given grp/src/mask */
         vifi = oid->oid_elements[oid->oid_nelem-1] + 1;
         if (!get_address(oid, &grp, ot->ot_name->oid_nelem)
          || !get_address(oid, &src, ot->ot_name->oid_nelem+4)
          || !get_address(oid, &mask, ot->ot_name->oid_nelem+8)
          || !next_child(&gt, &st, grp, src, mask, &vifi))
            return NOTOK;

         put_address(oid, gt->gt_mcastgrp, ot->ot_name->oid_nelem);
         put_address(oid, st->st_origin, ot->ot_name->oid_nelem+4);
         put_address(oid, 0xFFFFFFFF, ot->ot_name->oid_nelem+8);
         oid->oid_elements[ot->ot_name->oid_nelem+12] = vifi;
         put_address(oid, gt->gt_mcastgrp, ot->ot_name->oid_nelem+13);
      }
      break;

	default:
	   return int_SNMP_error__status_genErr;
   }

   switch (ifvar) {

      case ipMRouteNextHopState:
         return o_integer (oi, v, (VIFM_ISSET(vifi, gt->gt_grpmems))? 2 : 1);

      /* Currently equal to ipMRouteUpTime */
      case ipMRouteNextHopUpTime: {
         time_t currtime;
         time(&currtime);
         return o_integer (oi, v, (currtime - gt->gt_ctime)*100);
      }

      case ipMRouteNextHopExpiryTime:
         return o_integer (oi, v, gt->gt_prsent_timer);

      case ipMRouteNextHopClosestMemberHops:
         return o_integer (oi, v, 0);

      case ipMRouteNextHopProtocol:
         return o_integer (oi, v, 4);

      default:
         return int_SNMP_error__status_noSuchName;
   }
}
