bosun.org@v0.0.0-20210513094433-e25bc3e69a1f/cmd/scollector/collectors/snmp_bridge.go (about)

     1  package collectors
     2  
     3  import (
     4  	"encoding/hex"
     5  	"encoding/json"
     6  	"fmt"
     7  	"sort"
     8  	"strings"
     9  	"time"
    10  
    11  	"bosun.org/cmd/scollector/conf"
    12  	"bosun.org/metadata"
    13  	"bosun.org/opentsdb"
    14  	"bosun.org/slog"
    15  )
    16  
    17  const (
    18  	vtpVlanState         = "1.3.6.1.4.1.9.9.46.1.3.1.1.2.1"
    19  	dot1dTpFdbAddress    = "1.3.6.1.2.1.17.4.3.1.1"
    20  	dot1dTpFdbPort       = "1.3.6.1.2.1.17.4.3.1.2"
    21  	dot1dBasePortIfIndex = "1.3.6.1.2.1.17.1.4.1.2"
    22  )
    23  
    24  // SNMP Bridge registers
    25  func SNMPBridge(cfg conf.SNMP) {
    26  	collectors = append(collectors, &IntervalCollector{
    27  		F: func() (opentsdb.MultiDataPoint, error) {
    28  			return c_snmp_bridge(cfg.Community, cfg.Host)
    29  		},
    30  		Interval: time.Minute * 5,
    31  		name:     fmt.Sprintf("snmp-bridge-%s", cfg.Host),
    32  	})
    33  	collectors = append(collectors, &IntervalCollector{
    34  		F: func() (opentsdb.MultiDataPoint, error) {
    35  			return c_snmp_cdp(cfg.Community, cfg.Host)
    36  		},
    37  		Interval: time.Minute * 5,
    38  		name:     fmt.Sprintf("snmp-cdp-%s", cfg.Host),
    39  	})
    40  }
    41  
    42  func c_snmp_bridge(community, host string) (opentsdb.MultiDataPoint, error) {
    43  	var md opentsdb.MultiDataPoint
    44  	vlanRaw, err := snmp_subtree(host, community, vtpVlanState)
    45  	if err != nil {
    46  		return md, err
    47  	}
    48  	vlans := []string{}
    49  	for vlan, state := range vlanRaw {
    50  		Add(&md, "cisco.net.vlan_state", state, opentsdb.TagSet{"host": host, "vlan": vlan}, metadata.Gauge, metadata.StatusCode, "")
    51  		vlans = append(vlans, vlan)
    52  	}
    53  	ifMacs := make(map[string][]string)
    54  	for _, vlan := range vlans {
    55  		// community string indexing: http://www.cisco.com/c/en/us/support/docs/ip/simple-network-management-protocol-snmp/40367-camsnmp40367.html
    56  		macRaw, err := snmp_subtree(host, community+"@"+vlan, dot1dTpFdbAddress)
    57  		if err != nil {
    58  			slog.Infoln(err)
    59  			// continue since it might just be the one vlan
    60  			continue
    61  		}
    62  		remoteMacAddresses := make(map[string]string)
    63  		for k, v := range macRaw {
    64  			if ba, ok := v.([]byte); ok {
    65  				remoteMacAddresses[k] = strings.ToUpper(hex.EncodeToString(ba))
    66  			}
    67  		}
    68  		toPort := make(map[string]string)
    69  		toPortRaw, err := snmp_subtree(host, community+"@"+vlan, dot1dTpFdbPort)
    70  		if err != nil {
    71  			slog.Infoln(err)
    72  		}
    73  		for k, v := range toPortRaw {
    74  			toPort[k] = fmt.Sprintf("%v", v)
    75  		}
    76  		portToIfIndex := make(map[string]string)
    77  		portToIfIndexRaw, err := snmp_subtree(host, community+"@"+vlan, dot1dBasePortIfIndex)
    78  		for k, v := range portToIfIndexRaw {
    79  			portToIfIndex[k] = fmt.Sprintf("%v", v)
    80  		}
    81  		if err != nil {
    82  			slog.Infoln(err)
    83  		}
    84  		for port, mac := range remoteMacAddresses {
    85  			if port, ok := toPort[port]; ok {
    86  				if ifIndex, ok := portToIfIndex[port]; ok {
    87  					if _, ok := ifMacs[ifIndex]; ok {
    88  						ifMacs[ifIndex] = append(ifMacs[ifIndex], mac)
    89  					} else {
    90  						ifMacs[ifIndex] = []string{mac}
    91  					}
    92  				}
    93  			}
    94  		}
    95  	}
    96  	for iface, macs := range ifMacs {
    97  		sort.Strings(macs)
    98  		j, err := json.Marshal(macs)
    99  		if err != nil {
   100  			return md, nil
   101  		}
   102  		metadata.AddMeta("", opentsdb.TagSet{"host": host, "iface": iface}, "remoteMacs", string(j), false)
   103  	}
   104  	return md, nil
   105  }
   106  
   107  const (
   108  	cdpCacheDeviceId   = "1.3.6.1.4.1.9.9.23.1.2.1.1.6"
   109  	cdpCacheDevicePort = "1.3.6.1.4.1.9.9.23.1.2.1.1.7"
   110  )
   111  
   112  type cdpCacheEntry struct {
   113  	InterfaceId string `json:"-"`
   114  	DeviceId    string
   115  	DevicePort  string
   116  }
   117  
   118  func c_snmp_cdp(community, host string) (opentsdb.MultiDataPoint, error) {
   119  	var md opentsdb.MultiDataPoint
   120  	cdpEntries := make(map[string]*cdpCacheEntry)
   121  	deviceIdRaw, err := snmp_subtree(host, community, cdpCacheDeviceId)
   122  	if err != nil {
   123  		return md, err
   124  	}
   125  	for k, v := range deviceIdRaw {
   126  		ids := strings.Split(k, ".")
   127  		if len(ids) != 2 {
   128  			slog.Error("unexpected snmp cdpCacheEntry id")
   129  			continue
   130  		}
   131  		cdpEntries[ids[0]] = &cdpCacheEntry{}
   132  		cdpEntries[ids[0]].DeviceId = fmt.Sprintf("%s", v)
   133  		cdpEntries[ids[0]].InterfaceId = ids[1]
   134  	}
   135  	devicePortRaw, err := snmp_subtree(host, community, cdpCacheDevicePort)
   136  	for k, v := range devicePortRaw {
   137  		ids := strings.Split(k, ".")
   138  		if len(ids) != 2 {
   139  			slog.Error("unexpected snmp cdpCacheEntry id")
   140  			continue
   141  		}
   142  		if entry, ok := cdpEntries[ids[0]]; ok {
   143  			entry.DevicePort = fmt.Sprintf("%s", v)
   144  		}
   145  	}
   146  	byInterface := make(map[string][]*cdpCacheEntry)
   147  	for _, entry := range cdpEntries {
   148  		if _, ok := byInterface[entry.InterfaceId]; ok {
   149  			byInterface[entry.InterfaceId] = append(byInterface[entry.InterfaceId], entry)
   150  		} else {
   151  			byInterface[entry.InterfaceId] = []*cdpCacheEntry{entry}
   152  		}
   153  	}
   154  	for iface, entry := range byInterface {
   155  		j, err := json.Marshal(entry)
   156  		if err != nil {
   157  			return md, err
   158  		}
   159  		metadata.AddMeta("", opentsdb.TagSet{"host": host, "iface": iface}, "cdpCacheEntries", string(j), false)
   160  	}
   161  	if err != nil {
   162  		return md, nil
   163  	}
   164  	return md, nil
   165  }