* * * * *

             A quick note on embedding languages within languages

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

I have this program called ospfquery that not only retrieves OSPF (Open
Shortest Path First) specific SNMP (Simple Network Management Protocol) 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 MIB (Management
Information Base)s that ospfquery uses to retrieve the routing table. But by
dumping the entire SNMP tree, I was able to find equivalents.

Table: Routing table SNMP MIBs for Cisco and Riverstone
Cisco   Riverstone
------------------------------
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 (Florida Atlantic University) [4], I seem to dimly recall the
ability to embed SQL (Structured Query Language) 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 22^nd, 2010

I found a way to do what I wanted [5] …


[1] http://www.onboard.jetbrains.com/is1/articles/04/10/lop/
[2] http://www.nooranch.com/synaesmedia/wiki/wiki.cgi?LanguageOrientedProgramming
[3] gopher://gopher.conman.org/0Phlog:2007/01/15.2
[4] http://www.fau.edu/
[5] gopher://gopher.conman.org/0Phlog:2010/07/22.1

Email author at [email protected]