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.
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