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 26, 2007

A leaky domain specific language

Continuing on about that article, there's yet another reason to be wary of DSLs: The Law of Leaky Abstractions (which itself is an extension of the wrong mental model, to a degree).

Going back to my hypothetical SNMP extensions:

IPAddress destination[];
IPAddress mask[];
IPAddress nexthop[];
int       protocol[];
int       age[];
int       metric[];
int       type[];
OID       sys = SNMPv2-MIB::sysObjectID.0;

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

/* skip the printing part */

This time, I'll be concentrating on just querying one SNMP variable:

OID sys = SNMPv2-MIB::sysObjectID.0;

if (sys == SNMPv2-SMI::enterprises.5567.1.1)
  printf("Riverstone\n");
else if (sys == SNMPv2-SMI::enterprises.9.1)
  printf("Cisco\n");
else
  printf("Unknown\n");

I thought it would be instructive to write the minimum amount of code that actually does this, using the net-snmp library. And yes, it was instructive.

/********************************************
* to compile, install net-snmp, then
*
* gcc -o sysid sysid.c -lsnmp -lcrypto
*
*********************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include <net-snmp/net-snmp-config.h>
#include <net-snmp/utilities.h>
#include <net-snmp/net-snmp-includes.h>

/*************************************************************************/

int main(int argc,char *argv[])
{
  static oid       sysObjectId[] = { 1,3,6,1,2,1,1,2,0};
  static oid       rs[]          = { 1,3,6,1,4,1,5567 };
  static oid       cisco[]       = { 1,3,6,1,4,1,9 };
  netsnmp_session  session;
  netsnmp_session *ss;
  netsnmp_pdu     *pdu;
  netsnmp_pdu     *response;
  int             status;

  snmp_parse_args(argc,argv,&session,"",NULL);

  ss = snmp_open(&session);
  if (ss == NULL) exit(1);

  pdu = snmp_pdu_create(SNMP_MSG_GET);
  snmp_add_null_var(pdu,sysObjectId,9);
  status = snmp_synch_response(ss,pdu,&response);
  
  if ((status == STAT_SUCCESS) && (response->errstat == SNMP_ERR_NOERROR))
  {
    if (memcmp(response->variables->val.objid,rs,sizeof(rs)) == 0)
      printf("Riverstone\n");
    else if (memcmp(response->variables->val.objid,cisco,sizeof(cisco)) == 0)
      printf("Cisco\n");
    else
      printf("Unknown\n");
  }

  snmp_free_pdu(response);
  snmp_close(ss);
  return(EXIT_SUCCESS);
}

So, where are the leaks?

Well, there's creating the actual session, or some other way of specifying which router we're attempting to query and the credientials we need to actually obtain the information (and there are three versions of the SNMP protocol, so we need to specify that as well somewhere). In my code, that's hidden behind the call to snmp_parse_args(), which expects to parse the command line and creates a template “session” for us (and believe me, I tried to see if there was any other way to construct this template “session” and I couldn't find it in the hour or so I looked). This is leak number one.

Then there's the actual call itself. You create the message, fill it with the variable(s) you want, then send the query and get a response back. But, there are several things that can go wrong, which is leak number two.

The first thing is that we never get a response back—the SNMP library simply times out (is the router down? A bad network cable? Who knows?). That's reflected in the return code from snmp_synch_response(). But even if we get a response back, the far side, the device we're querying via SNMP, can return an error—perhaps it doesn't support the MIB we requesting. Or the response itself got corrupted on the way back. And this is still part of leak number two.

The primary line I'm trying to implement:

OID sys = SNMPv2-MIB::sysObjectID.0;

So far, there are three things (at least, possibly more) that could go wrong: I never get a reponse back, the response I get back is an error, or I get the actual data I requested. What now?

Well, my hypothetical langauge extension could just set sys to NULL, indicating an error, but what error? Well, I could just stuff something into errno (if I'm extending C, for example):

string contact  = SNMPv2-MIB::sysContact.0;
string name     = SNMPv2-MIB::sysName.0;
string location = SNMPv2-MIB::sysLocation.0;

if ((contact == NULL) || (name == NULL) || (location == NULL))
{
  if (errno == ETIMEOUT)	/* timeout */
    ...
  else if (errno == EMIBEXIST)	/* MIB not supported */
    ...
  else
    ...
}

But that's error detection, and that's a whole other ball of mud.

And setting aside I still haven't specified which device I'm querying, this has another leak—I should bundle these three queries into a single SNMP query for better (faster, less bandwidth intensive) performance. I suppose the “compiler” could recognize this and do the bundling for us, but that's some sophisticated programming for just a seemingly simple DSL.

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.