Re: Re-registering sysContact and sysLocation with subagent

Magnus Fromreide <[email protected]> Tue, 8 Feb 2022 18:47:32 +0100
Newsgroups gmane.network.net-snmp.devel
Message-ID <[email protected]>
On Tue, Feb 08, 2022 at 02:28:57PM +0000, Winston Gadsby wrote:
> It turns out I never did have it working.  I'm trying to reregister sysContact and/or sysLocation from my subagent running under a master agent.  I think reregistering a system oid via a subagent is impossible.

It is quite possible.

You should just set the priority.

The attached files can be compiled using

gcc netsnmptmp.348085.c sysContact.c -o sysContact -lnetsnmpagent -lnetsnmp

and then run like

./sysContact -f -Lo -Dagentx

(the arguments are just to make it run in the foreground and log agentx
 interactions)

The important part - as I said last week - is to use a lower priority than
the one the agent uses so the magic line is sysContact.c:27.

/MF

> I set up a sample subagent project in c as described here:  http://www.net-snmp.org/wiki/index.php/TUT:Writing_a_Subagent.  I set up debugging.  When I try to unregister an object using unregister_mib(), it eventually calls unregister_mib_context() with null context, which calls netsnmp_subtree_find().  This returns a subtree with a single oid {1,}, or this plus a subtree consisting of the newly registered element in the sample code, if I register the sample element first.  It tries to find the sysLocation node to unregister in this tree, which fails, since the returned tree only consists of objects I've registered in the subagent.  If I replace sysLocation with nstAgentSubagentObject (the newly registered element from the example code) it does successfully unregister.
> 
> I turned on debugging in snmpd for register_mib, to verify that sysLocation and sysContact are being registered in null context by the master agentx:
> 
> register_mib:
> Feb 08 09:01:25 emma snmpd[169114]: registering "mibII/sysLocation" at
> Feb 08 09:01:25 emma snmpd[169114]: iso.3.6.1.2.1.1.6
> Feb 08 09:01:25 emma snmpd[169114]:  with context "(null)"
> 
> I don't think it's possible to unregister or reregister sysLocation or sysContact from a subagent, if the node was initially registered in the master agentx.
> 
>      Winston
> 
> 
> 
> 
> ________________________________
> From: Winston Gadsby <[email protected]>
> Sent: Wednesday, February 2, 2022 1:29 PM
> To: Magnus Fromreide <[email protected]>
> Cc: Larry Hayes <[email protected]>; [email protected] <[email protected]>
> Subject: Re: Re-registering sysContact and sysLocation with subagent
> 
> You're right.  It looks like it's expecting an unsigned long array (c_oid_p in the python wrapper).  Here's what I did in python.  First, I defined the unregister_mib arguments for the ctypes translation:
> 
> $ libnsa.unregister_mib.argtypes = [c_oid_p, ctypes.c_size_t]
> $ libnsa.unregister_mib.restype = ctypes.c_int
> 
> Then I defined the oid vars:
> 
> $ oid = (c_oid * MAX_OID_LEN)()
> $ oid_len = ctypes.c_size_t(MAX_OID_LEN)
> 
> c_oid is the ctypes definition for oid, ctypes.c_ulong.  Then c_oid_p is a pointer to it.  Then I used read_objid to translate from the text to the unsigned long format:
> 
> $ libnsa.read_objid(b"SNMPv2-MIB::sysContact", ctypes.cast(ctypes.byref(oid), c_oid_p), ctypes.byref(oid_len))
> 
> The result looks correct:  [1, 3, 6, 1, 2, 1, 1, 4, ...].  Then I used this oid and oid_len in unregister_mib, translating the args appropriately:
> 
> $ result = libnsa.unregister_mib(ctypes.cast(ctypes.byref(oid), c_oid_p), oid_len)
> 
> The result is still always -1 (MIB_NOSUCHREGISTRATION).  However, it looks like it works, at least for sysContact.  If I ignore the error and reregister sysContact with my subagent it works.  I can change it in my program and see the change in a mib browser.
> 
> The very strange thing is I'm doing this with two separate, similar variables - sysContact and sysLocation.  SysContact always works, with the exception of the above error.  SysLocation always gives me another error when I start the agent:  registering pdu failed 263, and I am unable to change its value in my application.  When I started all this, and didn't do any unregister_mib calls, I'd get two of the "registering pdu failed 263" errors, I'm assuming one for sysContact and one for sysLocation.  So it looks like sysContact is now working, but sysLocation is still registered with the master agent.  I'm tracing this out and hoping it is a simple typo somewhere with sysLocation.
> 
> What type of agentx library did you have in mind?
> 
> Thanks again.
> 
>      Winston
> 
> 
> 
> 
> ________________________________
> From: Magnus Fromreide <[email protected]>
> Sent: Tuesday, February 1, 2022 8:56 PM
> To: Winston Gadsby <[email protected]>
> Cc: Larry Hayes <[email protected]>; [email protected] <[email protected]>
> Subject: Re: Re-registering sysContact and sysLocation with subagent
> 
> On Tue, Feb 01, 2022 at 02:58:14PM +0000, Winston Gadsby wrote:
> > Hi,
> > Thanks for the replies.  I'm working on trying to unregister the oid according to the link given below, using unregister_mib(oid, len).  I'm assuming the oid can be specified as a byte-string or integer array.  The difficulty I'm having is probably related to the ctypes translation mechanism from python.  Here's what I'm using in python.  The ctypes module enables the translation from the C libraries to python and imports the net-snmp space as libnsa:
> >
> > import ctypes
> > from netsnmpapi import *
> > import netsnmpvartypes
> > libnsa = ctypes.cdll.LoadLibrary(ctypes.util.find_library("netsnmpagent"))
> > ...
> > {code that registers oids from new mib}
> > ...
> > {start subagent}
> > print(libnsa.unregister_mib(b("SNMPv2-MIB::sysContact"), len("SNMPv2-MIB::sysContact"))
> >
> > This always returns -1, whether I unregister before starting the agent or after.  I've also tried unregistering some objects from my mib that were previously registered with the same result.
> 
> This looks like you are calling the unregister_mib c function in a dlopened
> version of netsnmp.
> 
> That function takes a parameter of type array of oid, what exact type oid is
> depends on how your netsnmp library was built but it usually is unsigned long.
> That array should contain the numerical OID value, in your case that would be
> { 1, 3, 6, 1, 2, 1, 1, 4 } but honestly I think an agentx library would serve
> you better.
> 
> /MF
> 
> 
> 
> >      Winston
> > ________________________________
> > From: Larry Hayes <[email protected]>
> > Sent: Friday, January 28, 2022 4:48 PM
> > To: Winston Gadsby <[email protected]>
> > Cc: [email protected] <[email protected]>
> > Subject: Re: Re-registering sysContact and sysLocation with subagent
> >
> > Someone just this month asked about taking control of the system description.  I would suspect that sysContact and sysLocation would follow the same way.
> >
> > Their solution was to unregister the OID then register your own handler.
> >
> >
> > unregister_mib() API.
> >
> > https://sourceforge.net/p/net-snmp/mailman/message/37598495/
> >
> >
> > It is a hassle, but you can also control the values of those inside of a snmpd.local.conf file.
> >
> > syscontact Nobody <[email protected]>
> >
> > Your  agent would write the values to that file then SIGHUP the snmpd to pick up the changes.
> >
> >
> > On Fri, Jan 28, 2022 at 3:37 PM Winston Gadsby <[email protected]<mailto:[email protected]>> wrote:
> > Hi,
> > I'm running a subagent based on python-netsnmpagent, along with the distribution snmpd (net-snmp) agent on a Debian embedded system.  I'm using the version of snmpd that comes default with the distribution - 5.7.3.  I have a custom MIB for my new variables, similar to SIMPLE-MIB.txt in the python-netsnmpagent example.  Through the standard snmp command line commands I am able to read and write to the standard MIB defined OIDs, and to the new OIDs in my MIB, without issue.
> >
> > I would like to also be able to take over management of sysContact and sysLocation through my subagent.  This makes it easier to control these variables through my outside gui.  I've tried registering SNMPv2-MIB::sysContact and SNMPv2-MIB::sysLocation through the agentx socket, but receive error 263 - duplicate registration.  It seems the primary agent has already registered these variables and won't give up control.
> >
> > How does the agent know which variables the subagent can register and which it cannot?  Is my error due to another cause?  My custom MIB is located in the same directory as the rest of the system MIBs, in the search path - /usr/share/snmp/mibs.  When I register a variable from my MIB, the master agent accepts it.  When I attempt to register sysContact or sysLocation it is denied.  If all the MIBs are stored together, how does it know my SIMPLE-MIB objects can be registered, but the others cannot?  Is there configuration somewhere else that determines which MIB variables I can register and which I cannot?  Am I only able to register variables in the new MIB?  How does it know which MIBs are custom and which are standard?  I've checked the snmpd.conf file, and it looks like I can define OID trees that trigger external programs, but there doesn't appear to be configuration for which OIDs are under master control and which can be controlled by the subagent, for normal snmp command line access.
> >
> > Is there perhaps a better way of going about doing this?
> >
> > I've gone through the mail lists and haven't been able to find an answer to this question.  Any help/pointers on this would be much appreciated.  Thank you,
> >
> > Winston
> >
> > _______________________________________________
> > Net-snmp-coders mailing list
> > [email protected]<mailto:[email protected]>
> > https://lists.sourceforge.net/lists/listinfo/net-snmp-coders
> 
> 
> > _______________________________________________
> > Net-snmp-coders mailing list
> > [email protected]
> > https://lists.sourceforge.net/lists/listinfo/net-snmp-coders
>

_______________________________________________
Net-snmp-coders mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/net-snmp-coders
netsnmptmp.348085.c (text/x-csrc, 7.1 KB)
/* generated from net-snmp-config */
#include <net-snmp/net-snmp-config.h>
#ifdef HAVE_SIGNAL
#include <signal.h>
#endif /* HAVE_SIGNAL */
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif /*  HAVE_SYS_STAT_H */
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif /* HAVE_FCNTL_H */
#include <unistd.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
  #include "sysContact.h"
const char *app_name = "sysContact";
static int reconfig = 0;

extern int netsnmp_running;

RETSIGTYPE
stop_server(int a) {
    netsnmp_running = 0;
}

#ifdef SIGHUP
RETSIGTYPE
hup_handler(int sig)
{
    reconfig = 1;
    signal(SIGHUP, hup_handler);
}
#endif

static void
usage(const char *prog)
{
    fprintf(stderr,
            "USAGE: %s [OPTIONS]\n"
            "\n"
            "OPTIONS:\n", prog);

    fprintf(stderr,
            "  -c FILE[,...]\t\tread FILE(s) as configuration file(s)\n"
            "  -C\t\t\tdo not read the default configuration files\n"
            "  -d\t\t\tdump all traffic\n"
            "  -D TOKEN[,...]\tturn on debugging output for the specified "
            "TOKENs\n"
            "\t\t\t   (ALL gives extremely verbose debugging output)\n"
            "  -f\t\t\tDo not fork() from the calling shell.\n"
            "  -h\t\t\tdisplay this help message\n"
            "  -H\t\t\tdisplay a list of configuration file directives\n"
            "  -L LOGOPTS\t\tToggle various defaults controlling logging:\n");
    snmp_log_options_usage("\t\t\t  ", stderr);
#ifndef DISABLE_MIB_LOADING
    fprintf(stderr,
            "  -m MIB[" ENV_SEPARATOR "...]\t\tload given list of MIBs (ALL loads "
            "everything)\n"
            "  -M DIR[" ENV_SEPARATOR "...]\t\tlook in given list of directories for MIBs\n");
#endif /* DISABLE_MIB_LOADING */
    fprintf(stderr,
            "  -p FILE\t\tstore process id in FILE\n");
#ifndef DISABLE_MIB_LOADING
    fprintf(stderr,
            "  -P MIBOPTS\t\tToggle various defaults controlling mib "
            "parsing:\n");
    snmp_mib_toggle_options_usage("\t\t\t  ", stderr);
#endif /* DISABLE_MIB_LOADING */
    fprintf(stderr,
            "  -v\t\t\tdisplay package version number\n"
            "  -x TRANSPORT\tconnect to master agent using TRANSPORT\n");
    exit(1);
}

static void
version(void)
{
    fprintf(stderr, "NET-SNMP version: %s\n", netsnmp_get_version());
    exit(0);
}

int
main (int argc, char **argv)
{
  int arg;
  char* cp = NULL;
  int dont_fork = 0, do_help = 0;
  char* pid_file = NULL;

  while ((arg = getopt(argc, argv, "c:CdD:fhHL:"
#ifndef DISABLE_MIB_LOADING
                       "m:M:"
#endif /* DISABLE_MIB_LOADING */
                       "n:p:"
#ifndef DISABLE_MIB_LOADING
                       "P:"
#endif /* DISABLE_MIB_LOADING */
                       "vx:")) != EOF) {
    switch (arg) {
      case 'c':
        if (optarg != NULL) {
          netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID,
                                NETSNMP_DS_LIB_OPTIONALCONFIG, optarg);
        } else {
          usage(argv[0]);
        }
        break;

      case 'C':
        netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID,
                               NETSNMP_DS_LIB_DONT_READ_CONFIGS, 1);
        break;

    case 'd':
      netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID,
                             NETSNMP_DS_LIB_DUMP_PACKET, 1);
      break;

    case 'D':
      debug_register_tokens(optarg);
      snmp_set_do_debugging(1);
      break;

    case 'f':
      dont_fork = 1;
      break;

    case 'h':
      usage(argv[0]);
      break;

    case 'H':
      do_help = 1;
      break;

    case 'L':
      if (snmp_log_options(optarg, argc, argv) < 0) {
        exit(1);
      }
      break;

#ifndef DISABLE_MIB_LOADING
    case 'm':
      if (optarg != NULL) {
        setenv("MIBS", optarg, 1);
      } else {
        usage(argv[0]);
      }
      break;

    case 'M':
      if (optarg != NULL) {
        setenv("MIBDIRS", optarg, 1);
      } else {
        usage(argv[0]);
      }
      break;
#endif /* DISABLE_MIB_LOADING */

    case 'n':
      if (optarg != NULL) {
        app_name = optarg;
        netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID,
                              NETSNMP_DS_LIB_APPTYPE, app_name);
      } else {
        usage(argv[0]);
      }
      break;

    case 'p':
      if (optarg != NULL) {
        pid_file = optarg;
      } else {
        usage(argv[0]);
      }
      break;

#ifndef DISABLE_MIB_LOADING
    case 'P':
      cp = snmp_mib_toggle_options(optarg);
      if (cp != NULL) {
        fprintf(stderr, "Unknown parser option to -P: %c.\n", *cp);
        usage(argv[0]);
      }
      break;
#endif /* DISABLE_MIB_LOADING */

    case 'v':
      version();
      break;

    case 'x':
      if (optarg != NULL) {
        netsnmp_ds_set_string(NETSNMP_DS_APPLICATION_ID,
                              NETSNMP_DS_AGENT_X_SOCKET, optarg);
      } else {
        usage(argv[0]);
      }
      break;

    default:
      fprintf(stderr, "invalid option: -%c\n", arg);
      usage(argv[0]);
      break;
    }
  }

  if (do_help) {
    netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID,
                           NETSNMP_DS_AGENT_NO_ROOT_ACCESS, 1);
  } else {
    /* we are a subagent */
    netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID,
                           NETSNMP_DS_AGENT_ROLE, 1);

    if (!dont_fork) {
      if (netsnmp_daemonize(1, snmp_stderrlog_status()) != 0)
        exit(1);
    }

#if HAVE_GETPID
    if (pid_file != NULL) {
      /*
       * unlink the pid_file, if it exists, prior to open.  Without
       * doing this the open will fail if the user specified pid_file
       * already exists.
       */
      int fd;
      unlink(pid_file);
      fd = open(pid_file, O_CREAT | O_EXCL | O_WRONLY, 0600);
      if (fd == -1) {
        snmp_log_perror(pid_file);
        if (!netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
                                    NETSNMP_DS_AGENT_NO_ROOT_ACCESS)) {
          exit(1);
        }
      } else {
        char buf[3 + sizeof(long) * 3];
        int len = snprintf(buf, sizeof(buf), "%ld\n", (long int)getpid());
        write(fd, buf, len);
        close(fd);
      }
    }
#endif

    /* initialize tcpip, if necessary */
    SOCK_STARTUP;
  }

  /* initialize the agent library */
  init_agent(app_name);

  /* initialize your mib code here */
  init_sysContact();

  /* sysContact will be used to read sysContact.conf files. */
  init_snmp("sysContact");

  if (do_help) {
    fprintf(stderr, "Configuration directives understood:\n");
    read_config_print_usage("  ");
    exit(0);
  }

  /* In case we received a request to stop (kill -TERM or kill -INT) */
  netsnmp_running = 1;
#ifdef SIGTERM
  signal(SIGTERM, stop_server);
#endif
#ifdef SIGINT
  signal(SIGINT, stop_server);
#endif
#ifdef SIGHUP
  signal(SIGHUP, hup_handler);
#endif

  /* main loop here... */
  while(netsnmp_running) {
    if (reconfig) {
      free_config();
      read_configs();
      reconfig = 0;
    }
    agent_check_and_process(1);
  }

  /* at shutdown time */
  snmp_shutdown(app_name);

  /* deinitialize your mib code here */

  /* shutdown the agent library */
  shutdown_agent();

  if (pid_file != NULL) {
    unlink(pid_file);
  }

  SOCK_CLEANUP;
  exit(0);
}
sysContact.c (text/x-csrc, 1.7 KB)
/*
 * Note: this file originally auto-generated by mib2c
 * using mib2c.scalar.conf
 */

#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
#include "sysContact.h"

struct pstring { unsigned char len; char data[128]; };
struct pstring sysContact = { 5, "nisse" };

/** Initializes the sysContact module */
void
init_sysContact(void)
{
    const oid       sysContact_oid[] = { 1, 3, 6, 1, 2, 1, 1, 4 };

    DEBUGMSGTL(("sysContact", "Initializing\n"));

    netsnmp_handler_registration* reg =
	netsnmp_create_handler_registration
            ("sysContact", handle_sysContact,
             sysContact_oid, OID_LENGTH(sysContact_oid),
             HANDLER_CAN_RONLY);
    reg->priority = 64;
    netsnmp_register_scalar(reg);
}

int
handle_sysContact(netsnmp_mib_handler *handler,
                  netsnmp_handler_registration *reginfo,
                  netsnmp_agent_request_info *reqinfo,
                  netsnmp_request_info *requests)
{
    /*
     * We are never called for a GETNEXT if it's registered as a
     * "instance", as it's "magically" handled for us.  
     */

    /*
     * a instance handler also only hands us one request at a time, so
     * we don't need to loop over a list of requests; we'll only get one. 
     */

    switch (reqinfo->mode) {

    case MODE_GET:
        snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
				 sysContact.data, sysContact.len);
        break;

    default:
        /*
         * we should never get here, so this is a really bad error 
         */
        snmp_log(LOG_ERR, "unknown mode (%d) in handle_sysContact\n",
                 reqinfo->mode);
        return SNMP_ERR_GENERR;
    }

    return SNMP_ERR_NOERROR;
}
sysContact.h (text/x-chdr, 295 B)
/*
 * Note: this file originally auto-generated by mib2c
 * using mib2c.scalar.conf
 */
#ifndef SYSCONTACT_H
#define SYSCONTACT_H

/*
 * function declarations 
 */
void            init_sysContact(void);
Netsnmp_Node_Handler handle_sysContact;

#endif                          /* SYSCONTACT_H */