Advertisement
Guest User

Untitled

a guest
Feb 24th, 2012
166
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Pike 11.04 KB | None | 0 0
  1. #! /usr/local/bin/pike
  2.  
  3. array switches = ({ "mdfswitch2",
  4.     "cab1switch1", "cab1switch2", "cab1switch3", "cab1switch4", "cab1switch5",
  5.     "cab2switch1", "cab3switch1", "cab4switch1", "cab4switch2", "cab5switch1",
  6.     "idf4eswitch3", "idf4eswitch4", "idf4eswitch5",
  7.     "idf4wswitch3", "idf4wswitch4", "idf4wswitch5",
  8.     "idf5eswitch3", "idf5eswitch4", "idf5eswitch5",
  9.     "idf5wswitch3", "idf5wswitch4", "idf5wswitch5"
  10. });
  11.  
  12. array ip = ({}), mac = ({}), bridgeport = ({}), switchport = ({}), all = ({});
  13.  
  14. int main(int argc, array(string) argv) {
  15.     object s = Sql.Sql("mysql://localhost/switchinfo");
  16.  
  17.     setup();
  18.  
  19.     s->query("update ip set current=0");
  20.     foreach (ip, mapping m)
  21.     {
  22.         s->query("replace into ip (switchname,portdescription,portname,vlan,hostname,ip,mac,macsonport,current) "
  23.             "values(%s,%s,%s,%d,%s,%s,%s,%d,%d)",
  24.             m->switchname, m->portdescription||"", m->portname||"",
  25.             m->vlan,
  26.             m->hostname, m->ip, m->dot1dTpFdbAddress, m->macsonport, 1);
  27.     };
  28.  
  29.     s->query("update mac set current=0");
  30.     foreach (mac, mapping m)
  31.     {
  32.         s->query("replace into mac (switchname,portdescription,portname,vlan,mac,macsonport,current) "
  33.             "values(%s,%s,%s,%d,%s,%d,%d)",
  34.             m->switchname, m->portdescription||"", m->portname||"",
  35.             m->vlan, m->dot1dTpFdbAddress, m->macsonport, 1);
  36.     };
  37.    
  38. }
  39.  
  40. /* inherit .portlist; */
  41.  
  42. array setup()
  43. {
  44.     mixed rv;
  45.     array tds = ({ });
  46.     array ip2 = ({ });
  47.  
  48.     /* Pull SNMP tables from all switches in parallel */
  49.     foreach (switches, string oneswitch)
  50.     {
  51.         tds += ({ Thread.Thread(fetch_switch, oneswitch) });
  52.     }
  53.  
  54.     /* Collect results */
  55.     foreach (tds, Thread t)
  56.     {
  57.         rv = t->wait();
  58.         ip += rv->ip;
  59.         mac += rv->mac;
  60.         bridgeport += rv->bridgeport;
  61.         switchport += rv->switchport;
  62.         all += rv->all;
  63.         write("]");
  64.     }
  65.     /* done. */
  66.     write("\n");
  67.  
  68.     /* For each IP we found, try and determine which port it's plugged
  69.        into */
  70.     foreach(Array.sort_array(Array.uniq(ip->ipNetToMediaNetAddress),Array.dwim_sort_func), string oneip)
  71.     {
  72.         mixed m;
  73.         int uniqs;
  74.  
  75.         /* Look up name */
  76.         mixed hostname = System.gethostbyaddr(oneip);
  77.         if (hostname)
  78.             hostname = hostname[0];
  79.         else
  80.             hostname = "";
  81.  
  82.         /* Get a list of all MACs associated with the IP */
  83.         array matches = findmatches(ip,
  84.             ([ "ipNetToMediaNetAddress":oneip ])
  85.             );
  86.  
  87.         uniqs = sizeof(Array.uniq(matches->ipNetToMediaPhysAddress));
  88.        
  89.         if (uniqs > 1)
  90.         {
  91.             /* Trouble.  Same IP seen with different macs on different
  92.              * switches.  NATting or routing is probably taking place.  If
  93.              * one of the macs maps to only one IP and the rest map to
  94.              * multiple IPs, pick the single MAC.
  95.              */
  96.  
  97.             /* First get a list of the macs seen on this IP */
  98.             mixed macs = Array.uniq(matches->ipNetToMediaPhysAddress);
  99.  
  100.             /* Now find all IP objects matching the macs we are interested
  101.              * in, and extract the mac associated with that IP.  We'll end
  102.              * up with an array of same macs as we started with, but with
  103.              * more dupes.
  104.              */
  105.            
  106.             macs = findmatches(ip, ([ "ipNetToMediaPhysAddress":macs ]))
  107.                 ->ipNetToMediaPhysAddress;
  108.  
  109.             macs = arraycount(macs);
  110.  
  111.             string least;
  112.            
  113.             /* Find the mac with the least other IPs assigned to it */
  114.             foreach(macs; string key; int count)
  115.             {
  116.                 if (key == "00:00:00:00:00:00")
  117.                     continue;
  118.                 if (!least || count < macs[least])
  119.                     least = key;
  120.             }
  121.  
  122.             /* filter our previous matches array to only include the mac we decided on */
  123.             matches = findmatches(matches,
  124.                 ([ "ipNetToMediaPhysAddress":least ])
  125.                 );
  126.                
  127.             uniqs = sizeof(Array.uniq(matches->ipNetToMediaPhysAddress));
  128.                
  129.             write ("Multiple MACs for %s %s; using %s (%d)\n", oneip, hostname, least, macs[least]);
  130.         }
  131.  
  132.         if (uniqs == 1)
  133.         {
  134.             /* Good, Only one MAC; now find the port with the least
  135.              number of MACs sharing it. */
  136.             mapping least;
  137.  
  138.             array macs = findmatches(mac,
  139.                 ([ "dot1dTpFdbAddress":matches[0]->ipNetToMediaPhysAddress ])
  140.                 );
  141.  
  142.             foreach(macs, mapping match)
  143.             {
  144.                 if (!least || (match->macsonport > 0 && match->macsonport < least->macsonport))
  145.                     least = match;
  146.             }
  147.  
  148.             if (!least)
  149.             {
  150.                 /* No ports routing this MAC? */
  151.                 write("Couldn't find any ports forwarding IP %s %s(%s)\n",
  152.                     oneip, hostname, matches[0]->ipNetToMediaPhysAddress);
  153.                 continue;
  154.             }
  155.  
  156.             m = copy_value(least);
  157.             m->ip = oneip;
  158.             m->secondpass = 1;
  159.             m->table = "ip";
  160.  
  161.             if (hostname)
  162.                 m->hostname = hostname;
  163.  
  164.             ip2 += ({ m });
  165.             all += ({ m });
  166.         } else
  167.         {
  168.             throw("shouldn't get here");
  169.         }
  170.        
  171.     }
  172.    
  173.     ip = ip2;
  174.    
  175.     return all;
  176. }
  177.  
  178. mixed fetch_switch(string switchname)
  179. {
  180.     array ip, mac, bridgeport, switchport, switchportX;
  181.  
  182.     write("[");
  183.    
  184.     ip = snmptable(switchname, "ipNetToMediaTable");
  185.     ip = ip[*] + ([ "table":"ip" ]);
  186.  
  187.     foreach(ip, mixed m)
  188.         m->ipNetToMediaPhysAddress = fixmac(m->ipNetToMediaPhysAddress);
  189.  
  190.     mac = snmptable(switchname, "dot1dTpFdbTable", (["allvlans":1]) );
  191.     mac = mac[*] + ([ "table":"mac" ]);
  192.  
  193.     foreach(mac, mixed m)
  194.         m->dot1dTpFdbAddress = fixmac(m->dot1dTpFdbAddress);
  195.  
  196.     bridgeport = snmptable(switchname, "dot1dBasePortTable", (["allvlans":1]));
  197.     bridgeport = bridgeport[*] + ([ "table":"bridgeport" ]);
  198.  
  199.     switchport = snmptable(switchname, "ifTable");
  200.     switchportX = snmptable(switchname, "ifXTable");
  201.  
  202.     /* merge iftable and ifxtable */
  203.     switchport = switchport[*] + switchportX[*];
  204.     switchport = switchport[*] + ([ "table":"switchport" ]);
  205.  
  206.     foreach(switchport, mixed m)
  207.         m->ifPhysAddress = fixmac(m->ifPhysAddress);
  208.  
  209.     /* Copy the physical port's name to each bridge port */
  210.     foreach(bridgeport, mixed m)
  211.     {
  212.         mixed sport = findmatches(switchport,
  213.             ([ "ifIndex":m->dot1dBasePortIfIndex ])
  214.             );
  215.         if (sizeof(sport) == 0)
  216.         {
  217.             ttyprintf("Cannot find the physical port for bridge port %s on %s\n",
  218.                 m->dot1dBasePortIfIndex, switchname);
  219.             continue;
  220.         }
  221.         if (sizeof(sport) > 1)
  222.         {
  223.             ttyprintf("Too many physical ports for bridge port %s on %s?\n",
  224.                 m->dot1dBasePortIfIndex, switchname);
  225.             continue;
  226.         }
  227.         sport = sport[0];
  228.         m->portdescription = sport->ifDescr;
  229.         m->portname = sport->ifName;
  230.     }
  231.  
  232.     /* Copy the port name and the number of other MACs on the port to
  233.        each MAC */
  234.     foreach(mac, mixed m)
  235.     {
  236.         array matches;
  237.         /* search bridgeport first */
  238.         matches = findmatches(bridgeport,
  239.             ([ "dot1dBasePort":m->dot1dTpFdbPort ])
  240.             );
  241.         if (sizeof(matches))
  242.         {
  243.             m->portname = matches[0]->portname;
  244.             m->portdescription = matches[0]->portdescription;
  245.             m->macsonport = sizeof(findmatches(mac, ([ "dot1dTpFdbPort":m->dot1dTpFdbPort ])));
  246.             continue;
  247.         }
  248.         /* no matches - try switchport */
  249.         matches = findmatches(switchport,
  250.             ([ "ifIndex":m->dot1dTpFdbPort ])
  251.             );
  252.         if (sizeof(matches))
  253.         {
  254.             m->portname = matches[0]->ifName;
  255.             m->portdescription = matches[0]->ifDescr;
  256.             m->macsonport = sizeof(findmatches(mac, ([ "dot1dTpFdbPort":m->dot1dTpFdbPort ])));
  257.             continue;
  258.         }
  259.     }
  260.  
  261.     /* Find out which port each known IP is visible on */
  262.     foreach(ip, mixed m)
  263.     {
  264.         array matches = findmatches(mac,
  265.             ([ "dot1dTpFdbAddress":m->ipNetToMediaPhysAddress ])
  266.             );
  267.         if (sizeof(matches) == 1)
  268.         {
  269.             m->portname = matches[0]->portname;
  270.             m->portdescription = matches[0]->portdescription;
  271.             m->macsonport = matches[0]->macsonport;
  272.         }
  273.     }
  274.  
  275.     write("%s ", switchname);
  276.  
  277.     return ([ "ip":ip, "mac":mac,
  278.         "switchport":switchport, "bridgeport":bridgeport,
  279.         "all":ip + mac + switchport + bridgeport ]);
  280.  
  281. }
  282.  
  283. array(mapping) snmptable(string|array switchname, string tablename, void|mixed options)
  284. {
  285.     if (!options)
  286.         options=([ ]);
  287.        
  288.     if (arrayp(switchname))
  289.     {
  290.         array rv = ({ });
  291.         array tds = ({ });
  292.         foreach (switchname, string onehost)
  293.         {
  294.             tds += ({ Thread.Thread(snmptable, onehost, tablename, options) });
  295.         }
  296.         foreach (tds, Thread t)
  297.         {
  298.             rv += t->wait();
  299.             write(".%s", switchname);
  300.         }
  301.         write ("\n");
  302.         return rv;
  303.     }
  304.  
  305.     if (options->allvlans)
  306.     {
  307.         m_delete(options, "allvlans");
  308.         array rv = ({ });
  309.  
  310.         /* Need to fetch a list of vlans known by this switch, and iterate
  311.             our table fetch over all of them */
  312.         mixed vlans=snmptable(switchname, "vtpVlanTable");
  313.  
  314.         /* No vlans? just do a regular pull */
  315.         if (sizeof(vlans) == 0)
  316.             return snmptable(switchname, tablename, options);
  317.        
  318.         foreach (vlans->index, mixed vlan)
  319.         {
  320.             /* just extract the final component of the vlan oid */
  321.             vlan=(int)((vlan/".")[-1]);
  322.        
  323.             /* ignore fake vlans */
  324.             if (vlan >= 1000)
  325.                 continue;
  326.            
  327.             /* fetch this vlan's table and tack it onto the rest */
  328.             rv += snmptable(switchname, tablename, options + ([ "vlan":vlan ]));
  329.             write("v%d",vlan);
  330.         }
  331.         return rv;
  332.     }
  333.  
  334.     string community = "-c public";
  335.    
  336.     if (options->vlan)
  337.         community += "@" + (string)options->vlan;
  338.  
  339.     string cmd = sprintf("snmptable -m +BRIDGE-MIB:CISCO-VTP-MIB -v2c -Ox -Ci -CB -Cf '\t' %s %s %s", community, switchname, tablename);
  340.  
  341.     mixed result = Process.popen(cmd);
  342.  
  343.     result /= "\n";
  344.     result = result[1..] - ({ "" });
  345.  
  346.     result[*] /= "\t";
  347.  
  348.     if (sizeof(result) < 2)
  349.         return ({ });
  350.  
  351.     array headers = result[0];
  352.     array rows = result[1..];
  353.  
  354.     array(mapping) table = mkmapping(headers, rows[*]);
  355.     table = table[*] + ([ "switchname":switchname ]);
  356.     table = table[*] + ([ "vlan":(options->vlan||1) ]);
  357.     return table;
  358. }
  359.  
  360. /* converts mac addresses into "aa:bb:cc:dd:ee:ff" format */
  361. string fixmac(string s)
  362. {
  363.     array vals;
  364.    
  365.     /* Extract runs of hex chars */
  366.     vals = Array.flatten(array_sscanf(s, "%{%*s%[a-fA-F0-9]%}")) - ({""});
  367.  
  368.     /* pad with zeros */
  369.     vals = map(vals, lambda(string v) { return sprintf("%02s", v); } );
  370.  
  371.     return lower_case(vals * ":");
  372. }
  373.  
  374. /* given an array of mappings 'input', return all entries that match the
  375.  * mappings in 'searches':
  376.  *
  377.  * ([ "*":"value1" ]) will match any input element with any value==value1
  378.  *
  379.  * ([ "key1": ({"value1","value2"}) ]) will match any input element with
  380.  *  key1==value1 or key1==value2
  381.  */
  382.  
  383. array findmatches(array input, mapping searches)
  384. {
  385.     return filter(input, lambda(mixed m)
  386.     {
  387.         foreach (searches; string key; string value)
  388.         {
  389. //          write("M:%O\nK:%O\nV:%O\n", m, key, value);
  390.             if (key)
  391.             {
  392.                 if (key == "*")
  393.                 {
  394.                     if (search(values(m), value)== -1)
  395.                         return 0;
  396.                 } else if (!arrayp(value))
  397.                 {
  398.                     /* Just searching for one thing */
  399. //                  write("M:%O\nK:%O\n", m, key);
  400.                     if (m[key] != value)
  401.                         return 0;
  402.                 } else
  403.                 {
  404. //                  write("V:%O\nm[key]:%O\n",value,m[key]);
  405.                     /* Search value is an array; match if any element matches */
  406.                     if (search(value, m[key]) == -1)
  407.                         return 0;
  408.                 }
  409.             } else
  410.             {
  411.                 /* Not even a mapping? */
  412.                 throw("what is this");
  413.                 if (search(m, value) == 0)
  414.                     return 0;
  415.             }
  416.         }
  417.         return 1;
  418.     });
  419. }
  420.  
  421. /* Take an array of duplicated lines, and return a mapping of
  422.  * element:count
  423.  */
  424.  
  425. mapping arraycount(array a)
  426. {
  427.     mapping rv = ([ ]);
  428.     foreach (a, mixed line)
  429.     {
  430.         rv[line] = Array.count(a,line);
  431.     }
  432.     return rv;
  433. }
  434.  
  435. int istty = !!Stdio.stdin->tcgetattr();
  436.  
  437. void ttyprintf(string format, mixed ... args)
  438. {
  439.     if (istty)
  440.         write(format, @args);
  441. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement