The Boston Diaries

The ongoing saga of a programmer who doesn't live in Boston, nor does he even like Boston, but yet named his weblog/journal “The Boston Diaries.”

Go figure.

Friday, January 19, 2007

A quick note on embedding languages within languages

While I don't fully agree with this article (link via ThoughtStorms, and more on this in a later entry) I do agree that our current methods of programming do fall short (design patterns being silly). This was made plainly apparent today at The Office.

I have this program called ospfquery that not only retrieves OSPF specific SNMP data, but can also dump a routing table from a router.

  Dest           Mask           NextHop         Proto   Metric Age
-------------------------------------------------------------------------------
169.254.0.0     255.255.0.0     XXXXXXXXXX.5    ospf    1     334866
172.16.0.0      255.240.0.0     XXXXXXXXXX.5    ospf    1     334866
192.0.2.0       255.255.255.0   XXXXXXXXXX.5    ospf    1     334866
192.168.0.0     255.255.0.0     XXXXXXXXXX.5    ospf    1     334866
198.18.0.0      255.254.0.0     XXXXXXXXXX.5    ospf    1     334866

But it doesn't quite work with Riverstones:

  Dest           Mask           NextHop         Proto   Metric Age
-------------------------------------------------------------------------------
1.0.0.0         32.161.7.0      26.127.177.69   ???     159791312426295

It's clear that the Riverstone doesn't support the right SNMP MIBs that ospfquery uses to retrieve the routing table. But by dumping the entire SNMP tree, I was able to find equivalents.

Routing table SNMP MIBs for Cisco and Riverstone
CiscoRiverstone
RFC1213-MIB::ipRouteDest IP-MIB::ip.24.4.1.1
RFC1213-MIB::ipRouteMask IP-MIB::ip.24.4.1.2
RFC1213-MIB::ipRouteNextHop IP-MIB::ip.24.4.1.4
RFC1213-MIB::ipRouteProto IP-MIB::ip.24.4.1.7
RFC1213-MIB::ipRouteAge IP-MIB::ip.24.4.1.8
RFC1213-MIB::ipRouteMetric1 IP-MIB::ip.24.4.1.11
RFC1213-MIB::ipRouteType IP-MIB::ip.24.4.1.6

So, on to the code:

oid objid_ipRouteDest[] = {1, 3, 6, 1, 2, 1, 4, 21, 1, 1};
int length_ipRouteDest = sizeof(objid_ipRouteDest)/sizeof(oid);
oid objid_ipRouteMetric1[] = {1, 3, 6, 1, 2, 1, 4, 21, 1, 3};
int length_ipRouteMetric1 = sizeof(objid_ipRouteMetric1)/sizeof(oid);
oid objid_ipRouteNextHop[] = {1, 3, 6, 1, 2, 1, 4, 21, 1, 7};
int length_ipRouteNextHop = sizeof(objid_ipRouteNextHop)/sizeof(oid);
oid objid_ipRouteType[] = {1, 3, 6, 1, 2, 1, 4, 21, 1, 8};
int length_ipRouteType = sizeof(objid_ipRouteType)/sizeof(oid);
oid objid_ipRouteProto[] = {1, 3, 6, 1, 2, 1, 4, 21, 1, 9};
int length_ipRouteProto = sizeof(objid_ipRouteProto)/sizeof(oid);
oid objid_ipRouteAge[] = {1, 3, 6, 1, 2, 1, 4, 21, 1, 10};
int length_ipRouteAge = sizeof(objid_ipRouteAge)/sizeof(oid);
oid objid_ipRouteMask[] = {1, 3, 6, 1, 2, 1, 4, 21, 1, 11};
int length_ipRouteMask = sizeof(objid_ipRouteMask)/sizeof(oid);


void collect_route_table(ss)
struct snmp_session *ss;
{
  struct ospfRT *cur;
  struct snmp_pdu *pdu, *response;
  struct variable_list *vars;
  int	    good_var, index;
  int	    status, count;

  pdu = snmp_pdu_create(GETNEXT_REQ_MSG);

  snmp_add_null_var(pdu, objid_ipRouteDest, length_ipRouteDest);
  snmp_add_null_var(pdu, objid_ipRouteMetric1, length_ipRouteMetric1);
  snmp_add_null_var(pdu, objid_ipRouteNextHop, length_ipRouteNextHop);
  snmp_add_null_var(pdu, objid_ipRouteType, length_ipRouteType);
  snmp_add_null_var(pdu, objid_ipRouteProto, length_ipRouteProto);
  snmp_add_null_var(pdu, objid_ipRouteAge, length_ipRouteAge);
  snmp_add_null_var(pdu, objid_ipRouteMask, length_ipRouteMask);

  good_var = 7;
  while(good_var == 7){
    good_var = 0;
    status = snmp_synch_response(ss, pdu, &response);
    if (status == STAT_SUCCESS){
      if (response->errstat == SNMP_ERR_NOERROR){
	pdu = snmp_pdu_create(GETNEXT_REQ_MSG);

	index = 0;
	if (rtp == (struct ospfRT *) 0) {
	    rtp = (struct ospfRT *) malloc(sizeof(struct ospfRT));
	    cur = rtp;
	    cur->prev = (struct ospfRT *) 0;
	}
	else {
	    cur->next = (struct ospfRT *) malloc(sizeof(struct ospfRT));
	    cur->next->prev = cur;
	    cur = cur->next;
	}
	cur->next = (struct ospfRT *) 0;
	for(vars = response->variables; vars; vars = vars->next_variable) {
          if (index == 0 && vars->name_length >= length_ipRouteDest &&
              !bcmp((char *)objid_ipRouteDest, (char *)vars->name,
              sizeof(objid_ipRouteDest))){
                cur->ipRouteDest = *vars->val.integer;
                snmp_add_null_var(pdu, vars->name, vars->name_length);
                good_var++;
          } else if (index == 1 && vars->name_length >= length_ipRouteMetric1 &&
                     !bcmp((char *)objid_ipRouteMetric1, (char *)vars->name,
                     sizeof(objid_ipRouteMetric1))){
            cur->ipRouteMetric1 = *vars->val.integer;
            snmp_add_null_var(pdu, vars->name, vars->name_length);
            good_var++;
          } else if (index == 2 && vars->name_length >= length_ipRouteNextHop &&
                     !bcmp((char *)objid_ipRouteNextHop, (char *)vars->name,
                     sizeof(objid_ipRouteNextHop))){
            cur->ipRouteNextHop = *vars->val.integer;
            snmp_add_null_var(pdu, vars->name, vars->name_length);
            good_var++;
          } else if (index == 3 && vars->name_length >= length_ipRouteType &&
                     !bcmp((char *)objid_ipRouteType, (char *)vars->name,
                     sizeof(objid_ipRouteType))){
              cur->ipRouteType = *vars->val.integer;
              snmp_add_null_var(pdu, vars->name, vars->name_length);
              good_var++;
          } else if (index == 4 && vars->name_length >= length_ipRouteProto &&
                     !bcmp((char *)objid_ipRouteProto, (char *)vars->name,
                     sizeof(objid_ipRouteProto))){
            cur->ipRouteProto = *vars->val.integer;
            snmp_add_null_var(pdu, vars->name, vars->name_length);
            good_var++;
          } else if (index == 5 && vars->name_length >= length_ipRouteAge &&
                     !bcmp((char *)objid_ipRouteAge, (char *)vars->name,
                     sizeof(objid_ipRouteAge))){
            cur->ipRouteAge = *vars->val.integer;
            snmp_add_null_var(pdu, vars->name, vars->name_length);
            good_var++;
          } else if (index == 6 && vars->name_length >= length_ipRouteMask &&
                     !bcmp((char *)objid_ipRouteMask, (char *)vars->name,
                     sizeof(objid_ipRouteMask))){
            cur->ipRouteMask = *vars->val.integer;
            snmp_add_null_var(pdu, vars->name, vars->name_length);
            good_var++;
          }
          index++;
        }
      } else {
        printf("Error in packet.\nReason: %s\n", snmp_errstring(response->errstat));
        if (response->errstat == SNMP_ERR_NOSUCHNAME){
          printf("This name doesn't exist: ");
          for(count = 1, vars = response->variables; vars && count != response->errindex;
              vars = vars->next_variable, count++)
			    ;
          if (vars)
            print_objid(vars->name, vars->name_length);
          printf("\n");
        }
      }

    } else if (status == STAT_TIMEOUT){
      printf("No Response from router\n");
      exit(1);
    } else {    /* status == STAT_ERROR */
      printf("An error occurred, Quitting\n");
      exit(2);
    }

    if (response)
      snmp_free_pdu(response);
  }
    /* get rid of last element that contains garbage. */
    /* this loop is ugly and copied from CMU. It needs rewritten */

  if (cur->prev) {
    cur->prev->next = (struct ospfRT *) 0;
    free(cur);
  }
}

Um … yeah … (and that's before adding support for the other set of SNMP MIBs)

Ideally, I'd love do to something like:

OID sys = SNMPv2-MIB::sysObjectID.0;
  
if (sys == SNMPv2-SMI::enterprises.5567.1.1)  /* riverstone */
{
  IpAddress destination[] = IP-MIB::ip.24.4.1.1;
  IpAddress mask[]        = IP-MIB::ip.24.4.1.2;
  IpAddress nexthop[]     = IP-MIB::ip.24.4.1.4;
  int       protocol[]    = IP-MIB::ip.24.4.1.7;
  int       age[]         = IP-MIB::ip.24.4.1.8;
  int       metric[]      = IP-MIB::ip.24.4.1.11;
  int       type[]        = IP-MIB::ip.24.4.1.6;
}
else if (sys == SNMPv2-SMI::enterprises.9.1)  /* cisco */
{
  IpAddress destination[] = RFC1213-MIB::ipRouteDest;
  IpAddress mask[]        = RFC1213-MIB::ipRouteMask;
  IpAddress nexthop[]     = RFC1213-MIB::ipRouteNextHop;
  int       protocol[]    = RFC1213-MIB::ipRouteProto;
  int       age[]         = RFC1213-MIB::ipRouteAge;
  int       metric[]      = RFC1213-MIB::ipRouteMetric1;
  int       type[]        = RFC1213-MIB::ipRouteType;
}

for (i = 0 ; i < destination.length; i++)
{
    print(
        destination[i],
        mask[i],
        nexthop[i],
        snmp.protocol(protocol[i]),
        metric[i],
        age[i]
    );
}

I would love to make SNMP queries like this, but the ability to embed a secondary language within another language is difficult at best. I do recall in college, I seem to dimly recall the ability to embed SQL within C (or was is Pascal? It was on the VAX system). Something like:

int authenticate(char *user,char *password)
{
  dbpass = ~~ SELECT password 
		FROM g_database.users 
		WHERE user="%user%" ~~;

  if (dbpass == NULL)
    return (FALSE);
  rc = (strcmp(dbpass,password) == 0);
  free(dbpass);
  return (rc);
}

Much easier than building SQL strings and hoping you get the quoting right.

But alas, such tools and/or languages don't exist (or have ceased to exist) and I'm stuck patching this program to support an alternative set of SNMP MIBs to pull the routing table from Riverstones.

Upate on Thursday, July 22nd, 2010

I found a way to do what I wanted

Obligatory Picture

[The future's so bright, I gotta wear shades]

Obligatory Contact Info

Obligatory Feeds

Obligatory Links

Obligatory Miscellaneous

You have my permission to link freely to any entry here. Go ahead, I won't bite. I promise.

The dates are the permanent links to that day's entries (or entry, if there is only one entry). The titles are the permanent links to that entry only. The format for the links are simple: Start with the base link for this site: https://boston.conman.org/, then add the date you are interested in, say 2000/08/01, so that would make the final URL:

https://boston.conman.org/2000/08/01

You can also specify the entire month by leaving off the day portion. You can even select an arbitrary portion of time.

You may also note subtle shading of the links and that's intentional: the “closer” the link is (relative to the page) the “brighter” it appears. It's an experiment in using color shading to denote the distance a link is from here. If you don't notice it, don't worry; it's not all that important.

It is assumed that every brand name, slogan, corporate name, symbol, design element, et cetera mentioned in these pages is a protected and/or trademarked entity, the sole property of its owner(s), and acknowledgement of this status is implied.

Copyright © 1999-2024 by Sean Conner. All Rights Reserved.